<commit_before>#include "perfmon.hpp"
#include "arch/arch.hpp"
#include "utils.hpp"
#include <stdarg.h>

/* The var list keeps track of all of the perfmon_t objects. */

intrusive_list_t<perfmon_t> &get_var_list() {

    /* Getter function so that we can be sure that var_list is initialized before it is needed,
    as advised by the C++ FAQ. Otherwise, a perfmon_t might be initialized before the var list
    was initialized. */

    static intrusive_list_t<perfmon_t> var_list;
    return var_list;
}

/* Class that wraps a pthread spinlock.

TODO: This should live in the arch/ directory. */

class spinlock_t {
    pthread_spinlock_t l;
public:
    spinlock_t() {
        pthread_spin_init(&l, PTHREAD_PROCESS_PRIVATE);
    }
    ~spinlock_t() {
        pthread_spin_destroy(&l);
    }
    void lock() {
        int res = pthread_spin_lock(&l);
        guarantee_err(res == 0, "could not lock spin lock");
    }
    void unlock() {
        int res = pthread_spin_unlock(&l);
        guarantee_err(res == 0, "could not unlock spin lock");
    }
};

spinlock_t &get_var_lock() {

    /* To avoid static initialization fiasco */

    static spinlock_t lock;
    return lock;
}

/* This is the function that actually gathers the stats. It is illegal to create or destroy
perfmon_t objects while a perfmon_fsm_t is active. */

struct perfmon_fsm_t :
    public home_cpu_mixin_t
{
    perfmon_callback_t *cb;
    perfmon_stats_t *dest;
    std::vector<void*> data;
    int messages_out;
    explicit perfmon_fsm_t(perfmon_stats_t *dest) : cb(NULL), dest(dest) {
        data.reserve(get_var_list().size());
        for (perfmon_t *p = get_var_list().head(); p; p = get_var_list().next(p)) {
            data.push_back(p->begin_stats());
        }
        messages_out = get_num_cpus();
        for (int i = 0; i < get_num_cpus(); i++) {
            do_on_cpu(i, this, &perfmon_fsm_t::visit);
        }
    }
    bool visit() {
        int i = 0;
        for (perfmon_t *p = get_var_list().head(); p; p = get_var_list().next(p)) {
            p->visit_stats(data[i++]);
        }
        do_on_cpu(home_cpu, this, &perfmon_fsm_t::have_visited);
        return true;
    }
    bool have_visited() {
        messages_out--;
        if (messages_out == 0) {
            int i = 0;
            for (perfmon_t *p = get_var_list().head(); p; p = get_var_list().next(p)) {
                p->end_stats(data[i++], dest);
            }
            if (cb) {
                cb->on_perfmon_stats();
                delete this;
            } else {
                /* Don't delete ourself; perfmon_get_stats() will delete us */
            }
        }
        return true;
    }
};

bool perfmon_get_stats(perfmon_stats_t *dest, perfmon_callback_t *cb) {

    perfmon_fsm_t *fsm = new perfmon_fsm_t(dest);
    if (fsm->messages_out == 0) {
        /* It has already finished */
        delete fsm;
        return true;
    } else {
        /* It has not finished yet */
        fsm->cb = cb;
        return false;
    }
}

/* Constructor and destructor register and deregister the perfmon.

Right now, it is illegal to make perfmon_t objects that are not static variables, so
we don't have to worry about locking the var list. If we locked it, then we could
create and destroy perfmon_ts at runtime. */

perfmon_t::perfmon_t()
{
    get_var_lock().lock();
    get_var_list().push_back(this);
    get_var_lock().unlock();
}

perfmon_t::~perfmon_t() {

    get_var_lock().lock();
    get_var_list().remove(this);
    get_var_lock().unlock();
}

/* perfmon_counter_t */

perfmon_counter_t::perfmon_counter_t(std::string name)
    : name(name)
{
    for (int i = 0; i < MAX_CPUS; i++) values[i] = 0;
}

int64_t &perfmon_counter_t::get() {
    return values[get_cpu_id()];
}

void *perfmon_counter_t::begin_stats() {
    return new int64_t[get_num_cpus()];
}

void perfmon_counter_t::visit_stats(void *data) {
    ((int64_t *)data)[get_cpu_id()] = get();
}

void perfmon_counter_t::end_stats(void *data, perfmon_stats_t *dest) {
    int64_t value = 0;
    for (int i = 0; i < get_num_cpus(); i++) value += ((int64_t *)data)[i];
    (*dest)[name] = format(value);
    delete[] (int64_t *)data;
}

/* perfmon_sampler_t */

perfmon_sampler_t::perfmon_sampler_t(std::string name, ticks_t length, bool include_rate)
    : name(name), length(length), include_rate(include_rate) { }

void perfmon_sampler_t::expire() {
    ticks_t now = get_ticks();
    std::deque<sample_t> &queue = values[get_cpu_id()];
    while (!queue.empty() && queue.front().timestamp + length < now) queue.pop_front();
}

void perfmon_sampler_t::record(value_t v) {
    expire();
    values[get_cpu_id()].push_back(sample_t(v, get_ticks()));
}

struct perfmon_sampler_step_t {
    uint64_t counts[MAX_CPUS];
    perfmon_sampler_t::value_t values[MAX_CPUS], mins[MAX_CPUS], maxes[MAX_CPUS];
};

void *perfmon_sampler_t::begin_stats() {
    return new perfmon_sampler_step_t;
}

void perfmon_sampler_t::visit_stats(void *data) {
    perfmon_sampler_step_t *d = (perfmon_sampler_step_t *)data;
    expire();
    d->values[get_cpu_id()] = 0;
    d->counts[get_cpu_id()] = 0;
    for (std::deque<perfmon_sampler_t::sample_t>::iterator it = values[get_cpu_id()].begin();
         it != values[get_cpu_id()].end(); it++) {
        d->values[get_cpu_id()] += (*it).value;
        if (d->counts[get_cpu_id()] > 0) {
            d->mins[get_cpu_id()] = std::min(d->mins[get_cpu_id()], (*it).value);
            d->maxes[get_cpu_id()] = std::max(d->maxes[get_cpu_id()], (*it).value);
        } else {
            d->mins[get_cpu_id()] = (*it).value;
            d->maxes[get_cpu_id()] = (*it).value;
        }
        d->counts[get_cpu_id()]++;
    }
}

void perfmon_sampler_t::end_stats(void *data, perfmon_stats_t *dest) {
    perfmon_sampler_step_t *d = (perfmon_sampler_step_t *)data;
    perfmon_sampler_t::value_t value = 0;
    uint64_t count = 0;
    perfmon_sampler_t::value_t min = 0, max = 0;   /* Initializers to make GCC shut up */
    bool have_any = false;
    for (int i = 0; i < get_num_cpus(); i++) {
        value += d->values[i];
        count += d->counts[i];
        if (d->counts[i]) {
            if (have_any) {
                min = std::min(d->mins[i], min);
                max = std::max(d->maxes[i], max);
            } else {
                min = d->mins[i];
                max = d->maxes[i];
                have_any = true;
            }
        }
    }
    if (have_any) {
        (*dest)[name + "_avg"] = format(value / count);
        (*dest)[name + "_min"] = format(min);
        (*dest)[name + "_max"] = format(max);
    } else {
        (*dest)[name + "_avg"] = "-";
        (*dest)[name + "_min"] = "-";
        (*dest)[name + "_max"] = "-";
    }
    if (include_rate) {
        (*dest)[name + "_persec"] = format(count / ticks_to_secs(length));
    }
    delete d;
};

<commit_msg>Added a comment.<commit_after>#include "perfmon.hpp"
#include "arch/arch.hpp"
#include "utils.hpp"
#include <stdarg.h>

/* The var list keeps track of all of the perfmon_t objects. */

intrusive_list_t<perfmon_t> &get_var_list() {

    /* Getter function so that we can be sure that var_list is initialized before it is needed,
    as advised by the C++ FAQ. Otherwise, a perfmon_t might be initialized before the var list
    was initialized. */

    static intrusive_list_t<perfmon_t> var_list;
    return var_list;
}

/* Class that wraps a pthread spinlock.

TODO: This should live in the arch/ directory. */

class spinlock_t {
    pthread_spinlock_t l;
public:
    spinlock_t() {
        pthread_spin_init(&l, PTHREAD_PROCESS_PRIVATE);
    }
    ~spinlock_t() {
        pthread_spin_destroy(&l);
    }
    void lock() {
        int res = pthread_spin_lock(&l);
        guarantee_err(res == 0, "could not lock spin lock");
    }
    void unlock() {
        int res = pthread_spin_unlock(&l);
        guarantee_err(res == 0, "could not unlock spin lock");
    }
};

/* The var lock protects the var list when it is being modified. In theory, this should all work
automagically because the constructor of every perfmon_t calls get_var_lock(), causing the var lock
to be constructed before the first perfmon, so it is destroyed after the last perfmon. */

spinlock_t &get_var_lock() {

    /* To avoid static initialization fiasco */

    static spinlock_t lock;
    return lock;
}

/* This is the function that actually gathers the stats. It is illegal to create or destroy
perfmon_t objects while a perfmon_fsm_t is active. */

struct perfmon_fsm_t :
    public home_cpu_mixin_t
{
    perfmon_callback_t *cb;
    perfmon_stats_t *dest;
    std::vector<void*> data;
    int messages_out;
    explicit perfmon_fsm_t(perfmon_stats_t *dest) : cb(NULL), dest(dest) {
        data.reserve(get_var_list().size());
        for (perfmon_t *p = get_var_list().head(); p; p = get_var_list().next(p)) {
            data.push_back(p->begin_stats());
        }
        messages_out = get_num_cpus();
        for (int i = 0; i < get_num_cpus(); i++) {
            do_on_cpu(i, this, &perfmon_fsm_t::visit);
        }
    }
    bool visit() {
        int i = 0;
        for (perfmon_t *p = get_var_list().head(); p; p = get_var_list().next(p)) {
            p->visit_stats(data[i++]);
        }
        do_on_cpu(home_cpu, this, &perfmon_fsm_t::have_visited);
        return true;
    }
    bool have_visited() {
        messages_out--;
        if (messages_out == 0) {
            int i = 0;
            for (perfmon_t *p = get_var_list().head(); p; p = get_var_list().next(p)) {
                p->end_stats(data[i++], dest);
            }
            if (cb) {
                cb->on_perfmon_stats();
                delete this;
            } else {
                /* Don't delete ourself; perfmon_get_stats() will delete us */
            }
        }
        return true;
    }
};

bool perfmon_get_stats(perfmon_stats_t *dest, perfmon_callback_t *cb) {

    perfmon_fsm_t *fsm = new perfmon_fsm_t(dest);
    if (fsm->messages_out == 0) {
        /* It has already finished */
        delete fsm;
        return true;
    } else {
        /* It has not finished yet */
        fsm->cb = cb;
        return false;
    }
}

/* Constructor and destructor register and deregister the perfmon.

Right now, it is illegal to make perfmon_t objects that are not static variables, so
we don't have to worry about locking the var list. If we locked it, then we could
create and destroy perfmon_ts at runtime. */

perfmon_t::perfmon_t()
{
    get_var_lock().lock();
    get_var_list().push_back(this);
    get_var_lock().unlock();
}

perfmon_t::~perfmon_t() {

    get_var_lock().lock();
    get_var_list().remove(this);
    get_var_lock().unlock();
}

/* perfmon_counter_t */

perfmon_counter_t::perfmon_counter_t(std::string name)
    : name(name)
{
    for (int i = 0; i < MAX_CPUS; i++) values[i] = 0;
}

int64_t &perfmon_counter_t::get() {
    return values[get_cpu_id()];
}

void *perfmon_counter_t::begin_stats() {
    return new int64_t[get_num_cpus()];
}

void perfmon_counter_t::visit_stats(void *data) {
    ((int64_t *)data)[get_cpu_id()] = get();
}

void perfmon_counter_t::end_stats(void *data, perfmon_stats_t *dest) {
    int64_t value = 0;
    for (int i = 0; i < get_num_cpus(); i++) value += ((int64_t *)data)[i];
    (*dest)[name] = format(value);
    delete[] (int64_t *)data;
}

/* perfmon_sampler_t */

perfmon_sampler_t::perfmon_sampler_t(std::string name, ticks_t length, bool include_rate)
    : name(name), length(length), include_rate(include_rate) { }

void perfmon_sampler_t::expire() {
    ticks_t now = get_ticks();
    std::deque<sample_t> &queue = values[get_cpu_id()];
    while (!queue.empty() && queue.front().timestamp + length < now) queue.pop_front();
}

void perfmon_sampler_t::record(value_t v) {
    expire();
    values[get_cpu_id()].push_back(sample_t(v, get_ticks()));
}

struct perfmon_sampler_step_t {
    uint64_t counts[MAX_CPUS];
    perfmon_sampler_t::value_t values[MAX_CPUS], mins[MAX_CPUS], maxes[MAX_CPUS];
};

void *perfmon_sampler_t::begin_stats() {
    return new perfmon_sampler_step_t;
}

void perfmon_sampler_t::visit_stats(void *data) {
    perfmon_sampler_step_t *d = (perfmon_sampler_step_t *)data;
    expire();
    d->values[get_cpu_id()] = 0;
    d->counts[get_cpu_id()] = 0;
    for (std::deque<perfmon_sampler_t::sample_t>::iterator it = values[get_cpu_id()].begin();
         it != values[get_cpu_id()].end(); it++) {
        d->values[get_cpu_id()] += (*it).value;
        if (d->counts[get_cpu_id()] > 0) {
            d->mins[get_cpu_id()] = std::min(d->mins[get_cpu_id()], (*it).value);
            d->maxes[get_cpu_id()] = std::max(d->maxes[get_cpu_id()], (*it).value);
        } else {
            d->mins[get_cpu_id()] = (*it).value;
            d->maxes[get_cpu_id()] = (*it).value;
        }
        d->counts[get_cpu_id()]++;
    }
}

void perfmon_sampler_t::end_stats(void *data, perfmon_stats_t *dest) {
    perfmon_sampler_step_t *d = (perfmon_sampler_step_t *)data;
    perfmon_sampler_t::value_t value = 0;
    uint64_t count = 0;
    perfmon_sampler_t::value_t min = 0, max = 0;   /* Initializers to make GCC shut up */
    bool have_any = false;
    for (int i = 0; i < get_num_cpus(); i++) {
        value += d->values[i];
        count += d->counts[i];
        if (d->counts[i]) {
            if (have_any) {
                min = std::min(d->mins[i], min);
                max = std::max(d->maxes[i], max);
            } else {
                min = d->mins[i];
                max = d->maxes[i];
                have_any = true;
            }
        }
    }
    if (have_any) {
        (*dest)[name + "_avg"] = format(value / count);
        (*dest)[name + "_min"] = format(min);
        (*dest)[name + "_max"] = format(max);
    } else {
        (*dest)[name + "_avg"] = "-";
        (*dest)[name + "_min"] = "-";
        (*dest)[name + "_max"] = "-";
    }
    if (include_rate) {
        (*dest)[name + "_persec"] = format(count / ticks_to_secs(length));
    }
    delete d;
};

<|endoftext|>


<commit_before>/*
 *  Main authors:
 *     Jason Nguyen <jason.nguyen@monash.edu>
 *     Guido Tack <guido.tack@monash.edu>
 */

/* 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/. */

#include <QJsonArray>
#include "process.h"
#include "ide.h"
#include "mainwindow.h"
#include "exception.h"

#ifndef Q_OS_WIN
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <iostream>
#endif

#ifdef Q_OS_WIN
#define pathSep ";"
#else
#define pathSep ":"
#endif

void Process::start(const QString &program, const QStringList &arguments, const QString &path)
{
    QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
    QString curPath = env.value("PATH");
    QString addPath = IDE::instance()->appDir();
    if (!path.isEmpty())
        addPath = path + pathSep + addPath;
    env.insert("PATH", addPath + pathSep + curPath);
    setProcessEnvironment(env);
#ifdef Q_OS_WIN
    _putenv_s("PATH", (addPath + pathSep + curPath).toStdString().c_str());
    jobObject = CreateJobObject(nullptr, nullptr);
    connect(this, SIGNAL(started()), this, SLOT(attachJob()));
#else
    setenv("PATH", (addPath + pathSep + curPath).toStdString().c_str(), 1);
#endif
    QProcess::start(program,arguments, QIODevice::Unbuffered | QIODevice::ReadWrite);
#ifdef Q_OS_WIN
    _putenv_s("PATH", curPath.toStdString().c_str());
#else
    setenv("PATH", curPath.toStdString().c_str(), 1);
#endif
}

void Process::terminate()
{
#ifdef Q_OS_WIN
    QString pipe;
    QTextStream ts(&pipe);
    ts << "\\\\.\\pipe\\minizinc-" << processId();
    auto pipeName = pipe.toStdString();
    HANDLE hNamedPipe = CreateFileA(pipeName.c_str(), GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
    if (hNamedPipe) {
        WriteFile(hNamedPipe, nullptr, 0, nullptr, nullptr);
        CloseHandle(hNamedPipe);
    }
#else
    ::killpg(processId(), SIGINT);
#endif
    if (!waitForFinished(500)) {
        if (state() != QProcess::NotRunning) {
#ifdef Q_OS_WIN
            TerminateJobObject(jobObject, EXIT_FAILURE);
#else
            ::killpg(processId(), SIGKILL);
#endif
            if (!waitForFinished(500)) {
                kill();
                waitForFinished();
            }
        }
    }
}

void Process::setupChildProcess()
{
#ifndef Q_OS_WIN
    if (::setpgid(0,0)) {
        std::cerr << "Error: Failed to create sub-process\n";
    }
#endif
}

#ifdef Q_OS_WIN
void Process::attachJob()
{
    AssignProcessToJobObject(jobObject, pid()->hProcess);
}
#endif

void MznDriver::setLocation(const QString &mznDistribPath)
{
    clear();

    _minizincExecutable = "minizinc";
    _mznDistribPath = mznDistribPath;

    MznProcess p;
    p.run({"--version"});
    if (!p.waitForStarted() || !p.waitForFinished()) {
        clear();
        throw ProcessError("Failed to find or launch MiniZinc executable.");
    }

    _versionString = p.readAllStandardOutput() + p.readAllStandardError();
    QRegularExpression version_regexp("version (\\d+)\\.(\\d+)\\.(\\d+)");
    QRegularExpressionMatch path_match = version_regexp.match(_versionString);
    if (path_match.hasMatch()) {
        _version = QVersionNumber(
                    path_match.captured(1).toInt(),
                    path_match.captured(2).toInt(),
                    path_match.captured(3).toInt()
        );
    } else {
        QString message = _versionString;
        clear();
        throw DriverError(message);
    }

    auto minVersion = QVersionNumber(2, 5, 0);
    if (_version < minVersion) {
        clear();
        throw DriverError("Versions of MiniZinc before " + minVersion.toString() + " are not supported.");
    }

    p.run({"--config-dirs"});
    if (p.waitForStarted() && p.waitForFinished()) {
        QString allOutput = p.readAllStandardOutput();
        QJsonDocument jd = QJsonDocument::fromJson(allOutput.toUtf8());
        if (!jd.isNull()) {
            QJsonObject sj = jd.object();
            _userSolverConfigDir = sj["userSolverConfigDir"].toString();
            _userConfigFile = sj["userConfigFile"].toString();
            _mznStdlibDir = sj["mznStdlibDir"].toString();
        }
    }
    p.run({"--solvers-json"});
    if (p.waitForStarted() && p.waitForFinished()) {
        QString allOutput = p.readAllStandardOutput();
        QJsonDocument jd = QJsonDocument::fromJson(allOutput.toUtf8());
        if (!jd.isNull()) {
            _solvers.clear();
            QJsonArray allSolvers = jd.array();
            for (auto item : allSolvers) {
                QJsonObject sj = item.toObject();
                Solver s(sj);
                _solvers.append(s);
            }
        }
    }
}

Solver* MznDriver::defaultSolver(void)
{
    for (auto& solver : solvers()) {
        if (solver.isDefaultSolver) {
            return &solver;
        }
    }
    return nullptr;
}

void MznDriver::setDefaultSolver(const Solver& s)
{
    for (auto& solver : solvers()) {
        solver.isDefaultSolver = &solver == &s;
    }

    QFile uc(userConfigFile());
    QJsonObject jo;
    if (uc.exists()) {
        if (uc.open(QFile::ReadOnly)) {
            QJsonDocument doc = QJsonDocument::fromJson(uc.readAll());
            if (doc.isNull()) {
                throw DriverError("Cannot modify user configuration file " + userConfigFile());
            }
            jo = doc.object();
            uc.close();
        }
    }
    QJsonArray tagdefs = jo.contains("tagDefaults") ? jo["tagDefaults"].toArray() : QJsonArray();
    bool hadDefault = false;
    for (int i=0; i<tagdefs.size(); i++) {
        if (tagdefs[i].isArray() && tagdefs[i].toArray()[0].isString() && tagdefs[i].toArray()[0].toString().isEmpty()) {
            QJsonArray def = tagdefs[i].toArray();
            def[1] = s.id;
            tagdefs[i] = def;
            hadDefault = true;
            break;
        }
    }
    if (!hadDefault) {
        QJsonArray def;
        def.append("");
        def.append(s.id);
        tagdefs.append(def);
    }
    jo["tagDefaults"] = tagdefs;
    QJsonDocument doc;
    doc.setObject(jo);
    QFileInfo uc_info(userConfigFile());
    if (!QDir().mkpath(uc_info.absoluteDir().absolutePath())) {
        throw DriverError("Cannot create user configuration directory " + uc_info.absoluteDir().absolutePath());
    }
    if (uc.open(QFile::ReadWrite | QIODevice::Truncate)) {
        uc.write(doc.toJson());
        uc.close();
    } else {
        throw DriverError("Cannot write user configuration file " + userConfigFile());
    }
}

MznProcess::MznProcess(QObject* parent) :
    Process(parent)
{
    connect(this, &MznProcess::started, [=] {
        elapsedTimer.start();
    });
    connect(this, QOverload<int, QProcess::ExitStatus>::of(&MznProcess::finished), [=](int, QProcess::ExitStatus) {
        onExited();
    });
    connect(this, &MznProcess::errorOccurred, [=](QProcess::ProcessError) {
        onExited();
    });
}

void MznProcess::run(const QStringList& args, const QString& cwd)
{
    Q_ASSERT(state() == NotRunning);
    setWorkingDirectory(cwd);
    Process::start(MznDriver::get().minizincExecutable(), args, MznDriver::get().mznDistribPath());
}

void MznProcess::run(const SolverConfiguration& sc, const QStringList& args, const QString& cwd)
{
    temp = new QTemporaryFile(QDir::tempPath() + "/mzn_XXXXXX.json");
    if (!temp->open()) {
        emit errorOccurred(QProcess::FailedToStart);
        return;
    }
    QString paramFile = temp->fileName();
    temp->write(sc.toJSON());
    temp->close();
    QStringList newArgs;
    newArgs << "--param-file" << paramFile << args;
    setWorkingDirectory(cwd);
    Process::start(MznDriver::get().minizincExecutable(), newArgs, MznDriver::get().mznDistribPath());
}

qint64 MznProcess::timeElapsed()
{
    if (elapsedTimer.isValid()) {
        return elapsedTimer.elapsed();
    }
    return finalTime;
}

void MznProcess::onExited()
{
    finalTime = elapsedTimer.elapsed();
    elapsedTimer.invalidate();
    delete temp;
    temp = nullptr;
}

SolveProcess::SolveProcess(QObject* parent) :
    MznProcess(parent)
{
    connect(this, &SolveProcess::readyReadStandardOutput, this, &SolveProcess::onStdout);
    connect(this, &SolveProcess::readyReadStandardError, this, &SolveProcess::onStderr);
    connect(this, QOverload<int, QProcess::ExitStatus>::of(&SolveProcess::finished), this, &SolveProcess::onFinished);
}

void SolveProcess::solve(const SolverConfiguration& sc, const QString& modelFile, const QStringList& dataFiles, const QStringList& extraArgs)
{
    state = State::Output;
    outputBuffer.clear();
    htmlBuffer.clear();
    jsonBuffer.clear();

    QFileInfo fi(modelFile);
    QStringList args;
    args << extraArgs << modelFile << dataFiles;
    MznProcess::run(sc, args, fi.canonicalPath());
}

void SolveProcess::onStdout()
{
    setReadChannel(QProcess::ProcessChannel::StandardOutput);

    while (canReadLine()) {
        auto line = QString::fromUtf8(readLine());
        processStdout(line);
    }
}

void SolveProcess::onStderr()
{
    setReadChannel(QProcess::ProcessChannel::StandardError);

    while (canReadLine()) {
        auto line = QString::fromUtf8(readLine());
        processStderr(line);
    }
}

void SolveProcess::processStdout(QString line)
{
    auto trimmed = line.trimmed();
    QRegExp jsonPattern("^(?:%%%(top|bottom))?%%%mzn-json(-init)?:(.*)");

    // Can appear in any state
    if (trimmed.startsWith("%%%mzn-stat")) {
        emit statisticOutput(line);
        return;
    } else if (trimmed.startsWith("%%%mzn-progress")) {
        auto split = trimmed.split(" ");
        bool ok = split.length() == 2;
        if (ok) {
            auto progress = split[1].toFloat(&ok);
            if (ok) {
                emit progressOutput(progress);
                return;
            }
        }
    }

    switch (state) {
    case State::Output: // Normal solution output
        if (trimmed == "----------") {
            outputBuffer << line;
            emit solutionOutput(outputBuffer.join(""));
            if (!visBuffer.isEmpty()) {
                emit jsonOutput(false, visBuffer);
                visBuffer.clear();
            }
            outputBuffer.clear();
        } else if (trimmed == "==========" || trimmed == "=====UNKNOWN=====" || trimmed == "=====ERROR=====") {
            outputBuffer << line;
            emit finalStatus(outputBuffer.join(""));
            outputBuffer.clear();
        } else if (jsonPattern.exactMatch(trimmed)) {
            auto captures = jsonPattern.capturedTexts();
            state = captures[2] == "-init" ? State::JSONInit : State::JSON;
            QString area = captures[1].isEmpty() ? "top" : captures[1];
            auto jsonArea = area == "bottom" ? Qt::BottomDockWidgetArea : Qt::TopDockWidgetArea;
            auto jsonPath = captures[3];
            visBuffer << VisOutput(VisWindowSpec(jsonPath, jsonArea));
        } else if (trimmed == "%%%mzn-html-start") {
            state = State::HTML;
        } else {
            outputBuffer << line;
        }
        break;
    case State::HTML: // Seen %%%mzn-html-start
        if (trimmed.startsWith("%%%mzn-html-end")) {
            emit htmlOutput(htmlBuffer.join(""));
            state = State::Output;
            htmlBuffer.clear();
        } else {
            htmlBuffer << line;
        }
        break;
    case State::JSONInit: // Seen %%%mzn-json-init
        if (trimmed == "%%%mzn-json-end") {
            visBuffer.last().data = jsonBuffer.join(" ");
            state = State::Output;
            jsonBuffer.clear();
        } else if (trimmed == "%%%mzn-json-init-end") {
            visBuffer.last().data = jsonBuffer.join(" ");
            emit jsonOutput(true, visBuffer);
            state = State::Output;
            jsonBuffer.clear();
            visBuffer.clear();
        } else {
            jsonBuffer << trimmed;
        }
        break;
    case State::JSON: // Seen %%%mzn-json
        if (trimmed == "%%%mzn-json-end") {
            visBuffer.last().data = jsonBuffer.join(" ");
            state = State::Output;
            jsonBuffer.clear();
        } else if (trimmed == "%%%mzn-json-time") {
            // Wrap current buffer in array with elapsed time
            jsonBuffer.prepend("[");
            jsonBuffer << "," << QString().number(timeElapsed()) << "]";
        } else {
            jsonBuffer << trimmed;
        }
        break;
    }
}

void SolveProcess::processStderr(QString line)
{
    auto trimmed = line.trimmed();
    emit stdErrorOutput(line);
}

void SolveProcess::onFinished(int exitCode, QProcess::ExitStatus status)
{
    // Finish processing remaining stdout
    onStdout();
    if (!atEnd()) {
        processStdout(readAll());
    }
    if (!outputBuffer.isEmpty()) {
        emit fragment(outputBuffer.join(""));
        outputBuffer.clear();
    }
    // Finish processing remaining stderr
    onStderr();
    if (!atEnd()) {
        processStderr(readAll());
    }

    emit complete(exitCode, status);
}
<commit_msg>Ensure stderr fragments appear when a newline is in stdout<commit_after>/*
 *  Main authors:
 *     Jason Nguyen <jason.nguyen@monash.edu>
 *     Guido Tack <guido.tack@monash.edu>
 */

/* 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/. */

#include <QJsonArray>
#include "process.h"
#include "ide.h"
#include "mainwindow.h"
#include "exception.h"

#ifndef Q_OS_WIN
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <iostream>
#endif

#ifdef Q_OS_WIN
#define pathSep ";"
#else
#define pathSep ":"
#endif

void Process::start(const QString &program, const QStringList &arguments, const QString &path)
{
    QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
    QString curPath = env.value("PATH");
    QString addPath = IDE::instance()->appDir();
    if (!path.isEmpty())
        addPath = path + pathSep + addPath;
    env.insert("PATH", addPath + pathSep + curPath);
    setProcessEnvironment(env);
#ifdef Q_OS_WIN
    _putenv_s("PATH", (addPath + pathSep + curPath).toStdString().c_str());
    jobObject = CreateJobObject(nullptr, nullptr);
    connect(this, SIGNAL(started()), this, SLOT(attachJob()));
#else
    setenv("PATH", (addPath + pathSep + curPath).toStdString().c_str(), 1);
#endif
    QProcess::start(program,arguments, QIODevice::Unbuffered | QIODevice::ReadWrite);
#ifdef Q_OS_WIN
    _putenv_s("PATH", curPath.toStdString().c_str());
#else
    setenv("PATH", curPath.toStdString().c_str(), 1);
#endif
}

void Process::terminate()
{
#ifdef Q_OS_WIN
    QString pipe;
    QTextStream ts(&pipe);
    ts << "\\\\.\\pipe\\minizinc-" << processId();
    auto pipeName = pipe.toStdString();
    HANDLE hNamedPipe = CreateFileA(pipeName.c_str(), GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
    if (hNamedPipe) {
        WriteFile(hNamedPipe, nullptr, 0, nullptr, nullptr);
        CloseHandle(hNamedPipe);
    }
#else
    ::killpg(processId(), SIGINT);
#endif
    if (!waitForFinished(500)) {
        if (state() != QProcess::NotRunning) {
#ifdef Q_OS_WIN
            TerminateJobObject(jobObject, EXIT_FAILURE);
#else
            ::killpg(processId(), SIGKILL);
#endif
            if (!waitForFinished(500)) {
                kill();
                waitForFinished();
            }
        }
    }
}

void Process::setupChildProcess()
{
#ifndef Q_OS_WIN
    if (::setpgid(0,0)) {
        std::cerr << "Error: Failed to create sub-process\n";
    }
#endif
}

#ifdef Q_OS_WIN
void Process::attachJob()
{
    AssignProcessToJobObject(jobObject, pid()->hProcess);
}
#endif

void MznDriver::setLocation(const QString &mznDistribPath)
{
    clear();

    _minizincExecutable = "minizinc";
    _mznDistribPath = mznDistribPath;

    MznProcess p;
    p.run({"--version"});
    if (!p.waitForStarted() || !p.waitForFinished()) {
        clear();
        throw ProcessError("Failed to find or launch MiniZinc executable.");
    }

    _versionString = p.readAllStandardOutput() + p.readAllStandardError();
    QRegularExpression version_regexp("version (\\d+)\\.(\\d+)\\.(\\d+)");
    QRegularExpressionMatch path_match = version_regexp.match(_versionString);
    if (path_match.hasMatch()) {
        _version = QVersionNumber(
                    path_match.captured(1).toInt(),
                    path_match.captured(2).toInt(),
                    path_match.captured(3).toInt()
        );
    } else {
        QString message = _versionString;
        clear();
        throw DriverError(message);
    }

    auto minVersion = QVersionNumber(2, 5, 0);
    if (_version < minVersion) {
        clear();
        throw DriverError("Versions of MiniZinc before " + minVersion.toString() + " are not supported.");
    }

    p.run({"--config-dirs"});
    if (p.waitForStarted() && p.waitForFinished()) {
        QString allOutput = p.readAllStandardOutput();
        QJsonDocument jd = QJsonDocument::fromJson(allOutput.toUtf8());
        if (!jd.isNull()) {
            QJsonObject sj = jd.object();
            _userSolverConfigDir = sj["userSolverConfigDir"].toString();
            _userConfigFile = sj["userConfigFile"].toString();
            _mznStdlibDir = sj["mznStdlibDir"].toString();
        }
    }
    p.run({"--solvers-json"});
    if (p.waitForStarted() && p.waitForFinished()) {
        QString allOutput = p.readAllStandardOutput();
        QJsonDocument jd = QJsonDocument::fromJson(allOutput.toUtf8());
        if (!jd.isNull()) {
            _solvers.clear();
            QJsonArray allSolvers = jd.array();
            for (auto item : allSolvers) {
                QJsonObject sj = item.toObject();
                Solver s(sj);
                _solvers.append(s);
            }
        }
    }
}

Solver* MznDriver::defaultSolver(void)
{
    for (auto& solver : solvers()) {
        if (solver.isDefaultSolver) {
            return &solver;
        }
    }
    return nullptr;
}

void MznDriver::setDefaultSolver(const Solver& s)
{
    for (auto& solver : solvers()) {
        solver.isDefaultSolver = &solver == &s;
    }

    QFile uc(userConfigFile());
    QJsonObject jo;
    if (uc.exists()) {
        if (uc.open(QFile::ReadOnly)) {
            QJsonDocument doc = QJsonDocument::fromJson(uc.readAll());
            if (doc.isNull()) {
                throw DriverError("Cannot modify user configuration file " + userConfigFile());
            }
            jo = doc.object();
            uc.close();
        }
    }
    QJsonArray tagdefs = jo.contains("tagDefaults") ? jo["tagDefaults"].toArray() : QJsonArray();
    bool hadDefault = false;
    for (int i=0; i<tagdefs.size(); i++) {
        if (tagdefs[i].isArray() && tagdefs[i].toArray()[0].isString() && tagdefs[i].toArray()[0].toString().isEmpty()) {
            QJsonArray def = tagdefs[i].toArray();
            def[1] = s.id;
            tagdefs[i] = def;
            hadDefault = true;
            break;
        }
    }
    if (!hadDefault) {
        QJsonArray def;
        def.append("");
        def.append(s.id);
        tagdefs.append(def);
    }
    jo["tagDefaults"] = tagdefs;
    QJsonDocument doc;
    doc.setObject(jo);
    QFileInfo uc_info(userConfigFile());
    if (!QDir().mkpath(uc_info.absoluteDir().absolutePath())) {
        throw DriverError("Cannot create user configuration directory " + uc_info.absoluteDir().absolutePath());
    }
    if (uc.open(QFile::ReadWrite | QIODevice::Truncate)) {
        uc.write(doc.toJson());
        uc.close();
    } else {
        throw DriverError("Cannot write user configuration file " + userConfigFile());
    }
}

MznProcess::MznProcess(QObject* parent) :
    Process(parent)
{
    connect(this, &MznProcess::started, [=] {
        elapsedTimer.start();
    });
    connect(this, QOverload<int, QProcess::ExitStatus>::of(&MznProcess::finished), [=](int, QProcess::ExitStatus) {
        onExited();
    });
    connect(this, &MznProcess::errorOccurred, [=](QProcess::ProcessError) {
        onExited();
    });
}

void MznProcess::run(const QStringList& args, const QString& cwd)
{
    Q_ASSERT(state() == NotRunning);
    setWorkingDirectory(cwd);
    Process::start(MznDriver::get().minizincExecutable(), args, MznDriver::get().mznDistribPath());
}

void MznProcess::run(const SolverConfiguration& sc, const QStringList& args, const QString& cwd)
{
    temp = new QTemporaryFile(QDir::tempPath() + "/mzn_XXXXXX.json");
    if (!temp->open()) {
        emit errorOccurred(QProcess::FailedToStart);
        return;
    }
    QString paramFile = temp->fileName();
    temp->write(sc.toJSON());
    temp->close();
    QStringList newArgs;
    newArgs << "--param-file" << paramFile << args;
    setWorkingDirectory(cwd);
    Process::start(MznDriver::get().minizincExecutable(), newArgs, MznDriver::get().mznDistribPath());
}

qint64 MznProcess::timeElapsed()
{
    if (elapsedTimer.isValid()) {
        return elapsedTimer.elapsed();
    }
    return finalTime;
}

void MznProcess::onExited()
{
    finalTime = elapsedTimer.elapsed();
    elapsedTimer.invalidate();
    delete temp;
    temp = nullptr;
}

SolveProcess::SolveProcess(QObject* parent) :
    MznProcess(parent)
{
    connect(this, &SolveProcess::readyReadStandardOutput, this, &SolveProcess::onStdout);
    connect(this, &SolveProcess::readyReadStandardError, this, &SolveProcess::onStderr);
    connect(this, QOverload<int, QProcess::ExitStatus>::of(&SolveProcess::finished), this, &SolveProcess::onFinished);
}

void SolveProcess::solve(const SolverConfiguration& sc, const QString& modelFile, const QStringList& dataFiles, const QStringList& extraArgs)
{
    state = State::Output;
    outputBuffer.clear();
    htmlBuffer.clear();
    jsonBuffer.clear();

    QFileInfo fi(modelFile);
    QStringList args;
    args << extraArgs << modelFile << dataFiles;
    MznProcess::run(sc, args, fi.canonicalPath());
}

void SolveProcess::onStdout()
{
    setReadChannel(QProcess::ProcessChannel::StandardOutput);

    // There might be a fragment on stderr without a new line
    // This should be shown now since stdout has a newline
    // to emulate console behaviour
    if (canReadLine()) {
        // All available stderr must be a fragment since otherwise
        // onStderr would have processed it already
        auto fragment = QString::fromUtf8(readAllStandardError());
        if (!fragment.isEmpty()) {
            processStderr(fragment);
        }
    }

    while (canReadLine()) {
        auto line = QString::fromUtf8(readLine());
        processStdout(line);
    }
}

void SolveProcess::onStderr()
{
    setReadChannel(QProcess::ProcessChannel::StandardError);

    while (canReadLine()) {
        auto line = QString::fromUtf8(readLine());
        processStderr(line);
    }
}

void SolveProcess::processStdout(QString line)
{
    auto trimmed = line.trimmed();
    QRegExp jsonPattern("^(?:%%%(top|bottom))?%%%mzn-json(-init)?:(.*)");

    // Can appear in any state
    if (trimmed.startsWith("%%%mzn-stat")) {
        emit statisticOutput(line);
        return;
    } else if (trimmed.startsWith("%%%mzn-progress")) {
        auto split = trimmed.split(" ");
        bool ok = split.length() == 2;
        if (ok) {
            auto progress = split[1].toFloat(&ok);
            if (ok) {
                emit progressOutput(progress);
                return;
            }
        }
    }

    switch (state) {
    case State::Output: // Normal solution output
        if (trimmed == "----------") {
            outputBuffer << line;
            emit solutionOutput(outputBuffer.join(""));
            if (!visBuffer.isEmpty()) {
                emit jsonOutput(false, visBuffer);
                visBuffer.clear();
            }
            outputBuffer.clear();
        } else if (trimmed == "==========" || trimmed == "=====UNKNOWN=====" || trimmed == "=====ERROR=====") {
            outputBuffer << line;
            emit finalStatus(outputBuffer.join(""));
            outputBuffer.clear();
        } else if (jsonPattern.exactMatch(trimmed)) {
            auto captures = jsonPattern.capturedTexts();
            state = captures[2] == "-init" ? State::JSONInit : State::JSON;
            QString area = captures[1].isEmpty() ? "top" : captures[1];
            auto jsonArea = area == "bottom" ? Qt::BottomDockWidgetArea : Qt::TopDockWidgetArea;
            auto jsonPath = captures[3];
            visBuffer << VisOutput(VisWindowSpec(jsonPath, jsonArea));
        } else if (trimmed == "%%%mzn-html-start") {
            state = State::HTML;
        } else {
            outputBuffer << line;
        }
        break;
    case State::HTML: // Seen %%%mzn-html-start
        if (trimmed.startsWith("%%%mzn-html-end")) {
            emit htmlOutput(htmlBuffer.join(""));
            state = State::Output;
            htmlBuffer.clear();
        } else {
            htmlBuffer << line;
        }
        break;
    case State::JSONInit: // Seen %%%mzn-json-init
        if (trimmed == "%%%mzn-json-end") {
            visBuffer.last().data = jsonBuffer.join(" ");
            state = State::Output;
            jsonBuffer.clear();
        } else if (trimmed == "%%%mzn-json-init-end") {
            visBuffer.last().data = jsonBuffer.join(" ");
            emit jsonOutput(true, visBuffer);
            state = State::Output;
            jsonBuffer.clear();
            visBuffer.clear();
        } else {
            jsonBuffer << trimmed;
        }
        break;
    case State::JSON: // Seen %%%mzn-json
        if (trimmed == "%%%mzn-json-end") {
            visBuffer.last().data = jsonBuffer.join(" ");
            state = State::Output;
            jsonBuffer.clear();
        } else if (trimmed == "%%%mzn-json-time") {
            // Wrap current buffer in array with elapsed time
            jsonBuffer.prepend("[");
            jsonBuffer << "," << QString().number(timeElapsed()) << "]";
        } else {
            jsonBuffer << trimmed;
        }
        break;
    }
}

void SolveProcess::processStderr(QString line)
{
    auto trimmed = line.trimmed();
    emit stdErrorOutput(line);
}

void SolveProcess::onFinished(int exitCode, QProcess::ExitStatus status)
{
    // Finish processing remaining stdout
    onStdout();
    if (!atEnd()) {
        processStdout(readAll());
    }
    if (!outputBuffer.isEmpty()) {
        emit fragment(outputBuffer.join(""));
        outputBuffer.clear();
    }
    // Finish processing remaining stderr
    onStderr();
    if (!atEnd()) {
        processStderr(readAll());
    }

    emit complete(exitCode, status);
}
<|endoftext|>


<commit_before>
#ifndef STAN_MATH_OPENCL_KERNELS_BERNOULLI_LOGIT_GLM_LPMF_HPP
#define STAN_MATH_OPENCL_KERNELS_BERNOULLI_LOGIT_GLM_LPMF_HPP
#ifdef STAN_OPENCL

#include <stan/math/opencl/kernel_cl.hpp>

namespace stan {
namespace math {
namespace opencl_kernels {

// \cond
static const char* bernoulli_logit_glm_kernel_code = STRINGIFY(
    // \endcond
    /** \ingroup opencl_kernels
     * GPU implementation of Generalized Linear Model (GLM)
     * with Bernoulli distribution and logit link function.
     *
     * Must be run with at least N threads and local size equal to LOCAL_SIZE_.
     * @param[out] logp_global partially summed log probability (1 value per
     * work group)
     * @param[out] theta_derivative_global derivative with respect to x * beta +
     * alpha
     * @param[out] theta_derivative_sum partially summed theta_derivative_global
     * (1 value per work group)
     * @param[in] y_global binary vector parameter
     * @param[in] x design matrix
     * @param[in] alpha intercept (in log odds)
     * @param[in] beta weight vector
     * @param N number of cases
     * @param M number of attributes
     * @param is_alpha_vector 0 or 1 - whether alpha is a vector (alternatively
     * it is a scalar)
     * @param need_theta_derivative interpreted as boolean - whether
     * theta_derivative needs to be computed
     * @param need_theta_derivative_sum interpreted as boolean - whether
     * theta_derivative_sum needs to be computed
     */
    __kernel void bernoulli_logit_glm(
        __global double* logp_global, __global double* theta_derivative_global,
        __global double* theta_derivative_sum, const __global int* y_global,
        const __global double* x, const __global double* alpha,
        const __global double* beta, const int N, const int M,
        const int is_alpha_vector, const int need_theta_derivative,
        const int need_theta_derivative_sum) {
      const int gid = get_global_id(0);
      const int lid = get_local_id(0);
      const int lsize = get_local_size(0);
      const int wg_id = get_group_id(0);

      __local double local_storage[LOCAL_SIZE_];

      double logp = 0;
      double theta_derivative = 0;
      // Most calculations only happen for relevant data within next if.
      // Exceptions are reductions between threads that need barriers.
      if (gid < N) {
        double ytheta = 0;
        for (int i = 0, j = 0; i < M; i++, j += N) {
          ytheta += x[j + gid] * beta[i];
        }
        const int y = y_global[gid];
        const double sign_ = 2 * y - 1;
        ytheta += alpha[gid * is_alpha_vector];
        ytheta *= sign_;
        if (y > 1 || y < 0 || !isfinite(ytheta)) {
          // this signals that an exception must be raised
          logp = NAN;
        }
        const double exp_m_ytheta = exp(-ytheta);

        const double cutoff = 20.0;
        if (ytheta > cutoff) {
          logp -= exp_m_ytheta;
          theta_derivative = -exp_m_ytheta;
        } else if (ytheta < -cutoff) {
          logp += ytheta;
          theta_derivative = sign_;
        } else {
          logp += -log1p(exp_m_ytheta);
          theta_derivative = sign_ * exp_m_ytheta / (exp_m_ytheta + 1);
        }

        if (need_theta_derivative) {
          theta_derivative_global[gid] = theta_derivative;
        }
      }
      // Sum logp, calculated by different threads.
      // Since we can't sum between different work groups, we emit one number
      // per work group. These must be summed on CPU for final result.
      local_storage[lid] = logp;
      barrier(CLK_LOCAL_MEM_FENCE);
      for (int step = lsize / REDUCTION_STEP_SIZE; step > 0;
           step /= REDUCTION_STEP_SIZE) {
        if (lid < step) {
          for (int i = 1; i < REDUCTION_STEP_SIZE; i++) {
            local_storage[lid] += local_storage[lid + step * i];
          }
        }
        barrier(CLK_LOCAL_MEM_FENCE);
      }
      if (lid == 0) {
        logp_global[wg_id] = local_storage[0];
      }

      if (need_theta_derivative_sum) {
        // Sum theta_derivative, calculated by different threads.
        barrier(CLK_LOCAL_MEM_FENCE);
        local_storage[lid] = theta_derivative;
        barrier(CLK_LOCAL_MEM_FENCE);
        for (int step = lsize / REDUCTION_STEP_SIZE; step > 0;
             step /= REDUCTION_STEP_SIZE) {
          if (lid < step) {
            for (int i = 1; i < REDUCTION_STEP_SIZE; i++) {
              local_storage[lid] += local_storage[lid + step * i];
            }
          }
          barrier(CLK_LOCAL_MEM_FENCE);
        }
        if (lid == 0) {
          theta_derivative_sum[wg_id] = local_storage[0];
        }
      }
    }
    // \cond
);
// \endcond

/** \ingroup opencl_kernels
 * See the docs for \link kernels/bernoulli_logit_glm_lpmf.hpp
 * bernoulli_logit_glm() \endlink
 */
const kernel_cl<out_buffer, out_buffer, out_buffer, in_buffer, in_buffer,
                in_buffer, in_buffer, int, int, int, int, int>
    bernoulli_logit_glm("bernoulli_logit_glm",
                        {bernoulli_logit_glm_kernel_code},
                        {{"REDUCTION_STEP_SIZE", 4}, {"LOCAL_SIZE_", 64}});

}  // namespace opencl_kernels
}  // namespace math
}  // namespace stan

#endif
#endif
<commit_msg>Revert "missed one file"<commit_after>
#ifndef STAN_MATH_OPENCL_KERNELS_BERNOULLI_LOGIT_GLM_LPMF_HPP
#define STAN_MATH_OPENCL_KERNELS_BERNOULLI_LOGIT_GLM_LPMF_HPP
#ifdef STAN_OPENCL

#include <stan/math/opencl/kernel_cl.hpp>

namespace stan {
namespace math {
namespace opencl_kernels {

// \cond
static const char* bernoulli_logit_glm_kernel_code = STRINGIFY(
    // \endcond
    /** \ingroup opencl_kernels
     * GPU implementation of Generalized Linear Model (GLM)
     * with Bernoulli distribution and logit link function.
     *
     * Must be run with at least N threads and local size equal to LOCAL_SIZE_.
     * @param[out] logp_global partially summed log probability (1 value per
     * work group)
     * @param[out] theta_derivative_global derivative with respect to x * beta +
     * alpha
     * @param[out] theta_derivative_sum partially summed theta_derivative_global
     * (1 value per work group)
     * @param[in] y_global binary vector parameter
     * @param[in] x design matrix
     * @param[in] alpha intercept (in log odds)
     * @param[in] beta weight vector
     * @param N number of cases
     * @param M number of attributes
     * @param is_y_vector 0 or 1 - whether y is a vector (alternatively
     * it is a scalar)
     * @param is_alpha_vector 0 or 1 - whether alpha is a vector (alternatively
     * it is a scalar)
     * @param need_theta_derivative interpreted as boolean - whether
     * theta_derivative needs to be computed
     * @param need_theta_derivative_sum interpreted as boolean - whether
     * theta_derivative_sum needs to be computed
     */
    __kernel void bernoulli_logit_glm(
        __global double* logp_global, __global double* theta_derivative_global,
        __global double* theta_derivative_sum, const __global int* y_global,
        const __global double* x, const __global double* alpha,
        const __global double* beta, const int N, const int M,
        const int is_y_vector, const int is_alpha_vector,
        const int need_theta_derivative, const int need_theta_derivative_sum) {
      const int gid = get_global_id(0);
      const int lid = get_local_id(0);
      const int lsize = get_local_size(0);
      const int wg_id = get_group_id(0);

      __local double local_storage[LOCAL_SIZE_];

      double logp = 0;
      double theta_derivative = 0;
      // Most calculations only happen for relevant data within next if.
      // Exceptions are reductions between threads that need barriers.
      if (gid < N) {
        double ytheta = 0;
        for (int i = 0, j = 0; i < M; i++, j += N) {
          ytheta += x[j + gid] * beta[i];
        }
        const int y = y_global[gid * is_y_vector];
        const double sign_ = 2 * y - 1;
        ytheta += alpha[gid * is_alpha_vector];
        ytheta *= sign_;
        if (y > 1 || y < 0 || !isfinite(ytheta)) {
          // this signals that an exception must be raised
          logp = NAN;
        }
        const double exp_m_ytheta = exp(-ytheta);

        const double cutoff = 20.0;
        if (ytheta > cutoff) {
          logp -= exp_m_ytheta;
          theta_derivative = -exp_m_ytheta;
        } else if (ytheta < -cutoff) {
          logp += ytheta;
          theta_derivative = sign_;
        } else {
          logp += -log1p(exp_m_ytheta);
          theta_derivative = sign_ * exp_m_ytheta / (exp_m_ytheta + 1);
        }

        if (need_theta_derivative) {
          theta_derivative_global[gid] = theta_derivative;
        }
      }
      // Sum logp, calculated by different threads.
      // Since we can't sum between different work groups, we emit one number
      // per work group. These must be summed on CPU for final result.
      local_storage[lid] = logp;
      barrier(CLK_LOCAL_MEM_FENCE);
      for (int step = lsize / REDUCTION_STEP_SIZE; step > 0;
           step /= REDUCTION_STEP_SIZE) {
        if (lid < step) {
          for (int i = 1; i < REDUCTION_STEP_SIZE; i++) {
            local_storage[lid] += local_storage[lid + step * i];
          }
        }
        barrier(CLK_LOCAL_MEM_FENCE);
      }
      if (lid == 0) {
        logp_global[wg_id] = local_storage[0];
      }

      if (need_theta_derivative_sum) {
        // Sum theta_derivative, calculated by different threads.
        barrier(CLK_LOCAL_MEM_FENCE);
        local_storage[lid] = theta_derivative;
        barrier(CLK_LOCAL_MEM_FENCE);
        for (int step = lsize / REDUCTION_STEP_SIZE; step > 0;
             step /= REDUCTION_STEP_SIZE) {
          if (lid < step) {
            for (int i = 1; i < REDUCTION_STEP_SIZE; i++) {
              local_storage[lid] += local_storage[lid + step * i];
            }
          }
          barrier(CLK_LOCAL_MEM_FENCE);
        }
        if (lid == 0) {
          theta_derivative_sum[wg_id] = local_storage[0];
        }
      }
    }
    // \cond
);
// \endcond

/** \ingroup opencl_kernels
 * See the docs for \link kernels/bernoulli_logit_glm_lpmf.hpp
 * bernoulli_logit_glm() \endlink
 */
const kernel_cl<out_buffer, out_buffer, out_buffer, in_buffer, in_buffer,
                in_buffer, in_buffer, int, int, int, int, int, int>
    bernoulli_logit_glm("bernoulli_logit_glm",
                        {bernoulli_logit_glm_kernel_code},
                        {{"REDUCTION_STEP_SIZE", 4}, {"LOCAL_SIZE_", 64}});

}  // namespace opencl_kernels
}  // namespace math
}  // namespace stan

#endif
#endif
<|endoftext|>


<commit_before>/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

#include <grpcpp/grpcpp.h>
#include <cstring>
#include <iostream>
#include <string>

#include "api/Compiler.hh"
#include "api/DataFile.hh"
#include "api/Decoder.hh"
#include "api/Encoder.hh"
#include "api/Generic.hh"
#include "api/Specific.hh"
#include "api/ValidSchema.hh"
#include "google/cloud/bigquery/storage/v1beta1/storage.grpc.pb.h"
#include "tensorflow/core/framework/op_kernel.h"
#include "tensorflow/core/lib/core/threadpool.h"
#include "tensorflow/core/lib/strings/stringprintf.h"
#include "tensorflow_io/bigquery/kernels/bigquery_lib.h"

namespace tensorflow {
namespace {

namespace apiv1beta1 = ::google::cloud::bigquery::storage::v1beta1;
static constexpr int kMaxReceiveMessageSize = 1 << 24;  // 16 MBytes

class BigQueryClientOp : public OpKernel {
 public:
  explicit BigQueryClientOp(OpKernelConstruction* ctx) : OpKernel(ctx) {
  }

  ~BigQueryClientOp() override {
    if (cinfo_.resource_is_private_to_kernel()) {
      if (!cinfo_.resource_manager()
               ->Delete<BigQueryClientResource>(cinfo_.container(),
                                                cinfo_.name())
               .ok()) {
        // Do nothing; the resource can have been deleted by session resets.
      }
    }
  }

  void Compute(OpKernelContext* ctx) override LOCKS_EXCLUDED(mu_) {
    mutex_lock l(mu_);
    if (!initialized_) {
      ResourceMgr* mgr = ctx->resource_manager();
      OP_REQUIRES_OK(ctx, cinfo_.Init(mgr, def()));
      BigQueryClientResource* resource;
      OP_REQUIRES_OK(
          ctx,
          mgr->LookupOrCreate<BigQueryClientResource>(
              cinfo_.container(), cinfo_.name(), &resource,
              [this, ctx](
                  BigQueryClientResource** ret) EXCLUSIVE_LOCKS_REQUIRED(mu_) {
                std::string server_name =
                    "dns:///bigquerystorage.googleapis.com";
                auto creds = ::grpc::GoogleDefaultCredentials();
                grpc::ChannelArguments args;
                args.SetMaxReceiveMessageSize(kMaxReceiveMessageSize);
                args.SetUserAgentPrefix("tensorflow");
                args.SetInt(GRPC_ARG_KEEPALIVE_PERMIT_WITHOUT_CALLS, 0);
                args.SetInt(GRPC_ARG_KEEPALIVE_TIMEOUT_MS, 60 * 1000);
                auto channel = ::grpc::CreateCustomChannel(
                    server_name, creds, args);
                VLOG(3) << "Creating GRPC channel";
                auto stub =
                    absl::make_unique<apiv1beta1::BigQueryStorage::Stub>(
                        channel);
                VLOG(3) << "Done creating GRPC channel";

                *ret = new BigQueryClientResource(std::move(stub));
                return Status::OK();
              }));
      core::ScopedUnref resource_cleanup(resource);
      initialized_ = true;
    }
    OP_REQUIRES_OK(ctx, MakeResourceHandleToOutput(
                            ctx, 0, cinfo_.container(), cinfo_.name(),
                            MakeTypeIndex<BigQueryClientResource>()));
  }

 private:
  mutex mu_;
  ContainerInfo cinfo_ GUARDED_BY(mu_);
  bool initialized_ GUARDED_BY(mu_) = false;
};

REGISTER_KERNEL_BUILDER(Name("IO>BigQueryClient").Device(DEVICE_CPU),
                        BigQueryClientOp);

class BigQueryReadSessionOp : public OpKernel {
 public:
  explicit BigQueryReadSessionOp(OpKernelConstruction* ctx) : OpKernel(ctx) {
    OP_REQUIRES_OK(ctx, ctx->GetAttr("parent", &parent_));
    OP_REQUIRES(ctx, !parent_.empty(),
                errors::InvalidArgument("parent must be non-empty"));

    OP_REQUIRES_OK(ctx, ctx->GetAttr("project_id", &project_id_));
    OP_REQUIRES(ctx, !project_id_.empty(),
                errors::InvalidArgument("project_id must be non-empty"));

    OP_REQUIRES_OK(ctx, ctx->GetAttr("table_id", &table_id_));
    OP_REQUIRES(ctx, !table_id_.empty(),
                errors::InvalidArgument("table_id must be non-empty"));

    OP_REQUIRES_OK(ctx, ctx->GetAttr("dataset_id", &dataset_id_));
    OP_REQUIRES(ctx, !dataset_id_.empty(),
                errors::InvalidArgument("dataset_id must be non-empty"));

    OP_REQUIRES_OK(ctx, ctx->GetAttr("selected_fields", &selected_fields_));
    OP_REQUIRES_OK(ctx, ctx->GetAttr("output_types", &output_types_));
    OP_REQUIRES_OK(ctx, ctx->GetAttr("row_restriction", &row_restriction_));
    OP_REQUIRES_OK(ctx, ctx->GetAttr("requested_streams", &requested_streams_));
  }

  void Compute(OpKernelContext* ctx) override LOCKS_EXCLUDED(mu_) {
    mutex_lock l(mu_);
    ResourceMgr* mgr = ctx->resource_manager();
    OP_REQUIRES_OK(ctx, cinfo_.Init(mgr, def()));

    BigQueryClientResource* client_resource;
    OP_REQUIRES_OK(
        ctx, LookupResource(ctx, HandleFromInput(ctx, 0), &client_resource));
    core::ScopedUnref scoped_unref(client_resource);

    apiv1beta1::CreateReadSessionRequest createReadSessionRequest;
    createReadSessionRequest.mutable_table_reference()->set_project_id(
        project_id_);
    createReadSessionRequest.mutable_table_reference()->set_dataset_id(
        dataset_id_);
    createReadSessionRequest.mutable_table_reference()->set_table_id(table_id_);
    createReadSessionRequest.set_parent(parent_);
    *createReadSessionRequest.mutable_read_options()
         ->mutable_selected_fields() = {selected_fields_.begin(),
                                        selected_fields_.end()};
    createReadSessionRequest.mutable_read_options()->set_row_restriction(
        row_restriction_);
    createReadSessionRequest.set_requested_streams(requested_streams_);
    createReadSessionRequest.set_format(apiv1beta1::DataFormat::AVRO);
    VLOG(3) << "createReadSessionRequest: "
            << createReadSessionRequest.DebugString();
    ::grpc::ClientContext context;
    context.AddMetadata("x-goog-request-params",
                        strings::Printf("table_reference.dataset_id=%s&table_"
                                        "reference.project_id=%s",
                                        dataset_id_.c_str(),
                                        project_id_.c_str()));
    context.set_deadline(gpr_time_add(gpr_now(GPR_CLOCK_REALTIME),
                         gpr_time_from_seconds(60, GPR_TIMESPAN)));

    std::shared_ptr<apiv1beta1::ReadSession> readSessionResponse =
        std::make_shared<apiv1beta1::ReadSession>();
    VLOG(3) << "calling readSession";
    ::grpc::Status status = client_resource->get_stub()->CreateReadSession(
        &context, createReadSessionRequest, readSessionResponse.get());
    if (!status.ok()) {
      VLOG(3) << "readSession status:" << GrpcStatusToString(status);
      ctx->CtxFailure(GrpcStatusToTfStatus(status));
      return;
    }
    VLOG(3) << "readSession response:" << readSessionResponse->DebugString();
    if (readSessionResponse->has_avro_schema()) {
      VLOG(3) << "avro schema:" << readSessionResponse->avro_schema().schema();
    }

    Tensor* streams_t = nullptr;
    OP_REQUIRES_OK(ctx, ctx->allocate_output(
        "streams",
        {readSessionResponse->streams_size()},
        &streams_t));
    auto streams_vec = streams_t->vec<string>();
    for (int i = 0; i < readSessionResponse->streams_size(); i++) {
      streams_vec(i) = readSessionResponse->streams(i).name();
    }
    Tensor* avro_schema_t = nullptr;
    OP_REQUIRES_OK(ctx,
                   ctx->allocate_output("avro_schema", {}, &avro_schema_t));
    avro_schema_t->scalar<string>()() =
        readSessionResponse->avro_schema().schema();
  }

 private:
  // Note: these fields are const after construction.
  string parent_;
  string project_id_;
  string table_id_;
  string dataset_id_;
  std::vector<string> selected_fields_;
  std::vector<DataType> output_types_;
  string row_restriction_;
  int requested_streams_;

  mutex mu_;
  ContainerInfo cinfo_ GUARDED_BY(mu_);
  bool initialized_ GUARDED_BY(mu_) = false;
};

REGISTER_KERNEL_BUILDER(Name("IO>BigQueryReadSession").Device(DEVICE_CPU),
                        BigQueryReadSessionOp);

}  // namespace
}  // namespace tensorflow
<commit_msg>Use balanced sharding strategy in bigquery read session. (#601)<commit_after>/* Copyright 2018 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

#include <grpcpp/grpcpp.h>
#include <cstring>
#include <iostream>
#include <string>

#include "api/Compiler.hh"
#include "api/DataFile.hh"
#include "api/Decoder.hh"
#include "api/Encoder.hh"
#include "api/Generic.hh"
#include "api/Specific.hh"
#include "api/ValidSchema.hh"
#include "google/cloud/bigquery/storage/v1beta1/storage.grpc.pb.h"
#include "tensorflow/core/framework/op_kernel.h"
#include "tensorflow/core/lib/core/threadpool.h"
#include "tensorflow/core/lib/strings/stringprintf.h"
#include "tensorflow_io/bigquery/kernels/bigquery_lib.h"

namespace tensorflow {
namespace {

namespace apiv1beta1 = ::google::cloud::bigquery::storage::v1beta1;
static constexpr int kMaxReceiveMessageSize = 1 << 24;  // 16 MBytes

class BigQueryClientOp : public OpKernel {
 public:
  explicit BigQueryClientOp(OpKernelConstruction* ctx) : OpKernel(ctx) {
  }

  ~BigQueryClientOp() override {
    if (cinfo_.resource_is_private_to_kernel()) {
      if (!cinfo_.resource_manager()
               ->Delete<BigQueryClientResource>(cinfo_.container(),
                                                cinfo_.name())
               .ok()) {
        // Do nothing; the resource can have been deleted by session resets.
      }
    }
  }

  void Compute(OpKernelContext* ctx) override LOCKS_EXCLUDED(mu_) {
    mutex_lock l(mu_);
    if (!initialized_) {
      ResourceMgr* mgr = ctx->resource_manager();
      OP_REQUIRES_OK(ctx, cinfo_.Init(mgr, def()));
      BigQueryClientResource* resource;
      OP_REQUIRES_OK(
          ctx,
          mgr->LookupOrCreate<BigQueryClientResource>(
              cinfo_.container(), cinfo_.name(), &resource,
              [this, ctx](
                  BigQueryClientResource** ret) EXCLUSIVE_LOCKS_REQUIRED(mu_) {
                std::string server_name =
                    "dns:///bigquerystorage.googleapis.com";
                auto creds = ::grpc::GoogleDefaultCredentials();
                grpc::ChannelArguments args;
                args.SetMaxReceiveMessageSize(kMaxReceiveMessageSize);
                args.SetUserAgentPrefix("tensorflow");
                args.SetInt(GRPC_ARG_KEEPALIVE_PERMIT_WITHOUT_CALLS, 0);
                args.SetInt(GRPC_ARG_KEEPALIVE_TIMEOUT_MS, 60 * 1000);
                auto channel = ::grpc::CreateCustomChannel(
                    server_name, creds, args);
                VLOG(3) << "Creating GRPC channel";
                auto stub =
                    absl::make_unique<apiv1beta1::BigQueryStorage::Stub>(
                        channel);
                VLOG(3) << "Done creating GRPC channel";

                *ret = new BigQueryClientResource(std::move(stub));
                return Status::OK();
              }));
      core::ScopedUnref resource_cleanup(resource);
      initialized_ = true;
    }
    OP_REQUIRES_OK(ctx, MakeResourceHandleToOutput(
                            ctx, 0, cinfo_.container(), cinfo_.name(),
                            MakeTypeIndex<BigQueryClientResource>()));
  }

 private:
  mutex mu_;
  ContainerInfo cinfo_ GUARDED_BY(mu_);
  bool initialized_ GUARDED_BY(mu_) = false;
};

REGISTER_KERNEL_BUILDER(Name("IO>BigQueryClient").Device(DEVICE_CPU),
                        BigQueryClientOp);

class BigQueryReadSessionOp : public OpKernel {
 public:
  explicit BigQueryReadSessionOp(OpKernelConstruction* ctx) : OpKernel(ctx) {
    OP_REQUIRES_OK(ctx, ctx->GetAttr("parent", &parent_));
    OP_REQUIRES(ctx, !parent_.empty(),
                errors::InvalidArgument("parent must be non-empty"));

    OP_REQUIRES_OK(ctx, ctx->GetAttr("project_id", &project_id_));
    OP_REQUIRES(ctx, !project_id_.empty(),
                errors::InvalidArgument("project_id must be non-empty"));

    OP_REQUIRES_OK(ctx, ctx->GetAttr("table_id", &table_id_));
    OP_REQUIRES(ctx, !table_id_.empty(),
                errors::InvalidArgument("table_id must be non-empty"));

    OP_REQUIRES_OK(ctx, ctx->GetAttr("dataset_id", &dataset_id_));
    OP_REQUIRES(ctx, !dataset_id_.empty(),
                errors::InvalidArgument("dataset_id must be non-empty"));

    OP_REQUIRES_OK(ctx, ctx->GetAttr("selected_fields", &selected_fields_));
    OP_REQUIRES_OK(ctx, ctx->GetAttr("output_types", &output_types_));
    OP_REQUIRES_OK(ctx, ctx->GetAttr("row_restriction", &row_restriction_));
    OP_REQUIRES_OK(ctx, ctx->GetAttr("requested_streams", &requested_streams_));
  }

  void Compute(OpKernelContext* ctx) override LOCKS_EXCLUDED(mu_) {
    mutex_lock l(mu_);
    ResourceMgr* mgr = ctx->resource_manager();
    OP_REQUIRES_OK(ctx, cinfo_.Init(mgr, def()));

    BigQueryClientResource* client_resource;
    OP_REQUIRES_OK(
        ctx, LookupResource(ctx, HandleFromInput(ctx, 0), &client_resource));
    core::ScopedUnref scoped_unref(client_resource);

    apiv1beta1::CreateReadSessionRequest createReadSessionRequest;
    createReadSessionRequest.mutable_table_reference()->set_project_id(
        project_id_);
    createReadSessionRequest.mutable_table_reference()->set_dataset_id(
        dataset_id_);
    createReadSessionRequest.mutable_table_reference()->set_table_id(table_id_);
    createReadSessionRequest.set_parent(parent_);
    *createReadSessionRequest.mutable_read_options()
         ->mutable_selected_fields() = {selected_fields_.begin(),
                                        selected_fields_.end()};
    createReadSessionRequest.mutable_read_options()->set_row_restriction(
        row_restriction_);
    createReadSessionRequest.set_requested_streams(requested_streams_);
    createReadSessionRequest.set_sharding_strategy(apiv1beta1::ShardingStrategy::BALANCED);
    createReadSessionRequest.set_format(apiv1beta1::DataFormat::AVRO);
    VLOG(3) << "createReadSessionRequest: "
            << createReadSessionRequest.DebugString();
    ::grpc::ClientContext context;
    context.AddMetadata("x-goog-request-params",
                        strings::Printf("table_reference.dataset_id=%s&table_"
                                        "reference.project_id=%s",
                                        dataset_id_.c_str(),
                                        project_id_.c_str()));
    context.set_deadline(gpr_time_add(gpr_now(GPR_CLOCK_REALTIME),
                         gpr_time_from_seconds(60, GPR_TIMESPAN)));

    std::shared_ptr<apiv1beta1::ReadSession> readSessionResponse =
        std::make_shared<apiv1beta1::ReadSession>();
    VLOG(3) << "calling readSession";
    ::grpc::Status status = client_resource->get_stub()->CreateReadSession(
        &context, createReadSessionRequest, readSessionResponse.get());
    if (!status.ok()) {
      VLOG(3) << "readSession status:" << GrpcStatusToString(status);
      ctx->CtxFailure(GrpcStatusToTfStatus(status));
      return;
    }
    VLOG(3) << "readSession response:" << readSessionResponse->DebugString();
    if (readSessionResponse->has_avro_schema()) {
      VLOG(3) << "avro schema:" << readSessionResponse->avro_schema().schema();
    }

    Tensor* streams_t = nullptr;
    OP_REQUIRES_OK(ctx, ctx->allocate_output(
        "streams",
        {readSessionResponse->streams_size()},
        &streams_t));
    auto streams_vec = streams_t->vec<string>();
    for (int i = 0; i < readSessionResponse->streams_size(); i++) {
      streams_vec(i) = readSessionResponse->streams(i).name();
    }
    Tensor* avro_schema_t = nullptr;
    OP_REQUIRES_OK(ctx,
                   ctx->allocate_output("avro_schema", {}, &avro_schema_t));
    avro_schema_t->scalar<string>()() =
        readSessionResponse->avro_schema().schema();
  }

 private:
  // Note: these fields are const after construction.
  string parent_;
  string project_id_;
  string table_id_;
  string dataset_id_;
  std::vector<string> selected_fields_;
  std::vector<DataType> output_types_;
  string row_restriction_;
  int requested_streams_;

  mutex mu_;
  ContainerInfo cinfo_ GUARDED_BY(mu_);
  bool initialized_ GUARDED_BY(mu_) = false;
};

REGISTER_KERNEL_BUILDER(Name("IO>BigQueryReadSession").Device(DEVICE_CPU),
                        BigQueryReadSessionOp);

}  // namespace
}  // namespace tensorflow
<|endoftext|>


<commit_before>#include <math.h>

#include "engine.h"
#include "entity.h"
#include "geometry.h"
#include "engine_event.h"
#include "misc.h"
#include "wall.h"
#include "player.h"
#include <SFML/Window/VideoMode.hpp>
#include <SFML/Window/Window.hpp>
#include <SFML/Graphics/Text.hpp>
#include <stdio.h>
#include "commands.h"

using namespace sf;

static bool interacts(Engine *engine, MoveContext &ctx, Entity *a, Entity *b);

void Engine::play(void) {
	while (step()) {
		Event e;
		while (window.pollEvent(e)) {
			if (e.type == Event::Closed) {
				quit();
			} else if (e.type == Event::KeyPressed) {
				if (e.key.code == Keyboard::Escape) quit();
			} else if (e.type == Event::JoystickConnected) {
				addPlayer(0, e.joystickConnect.joystickId);
			} else if (e.type == Event::JoystickDisconnected) {
				Player *pl = getPlayerByJoystickId(e.joystickConnect.joystickId);
				if (pl) {destroy(pl);destroy_flagged();}
			} else if (KeymapController::maybeConcerned(e)) {
				controller_activity(e);
			}
		}
	}
}

void Engine::controller_activity(Event &e) {
	for(EntitiesIterator it=entities.begin(); it != entities.end(); it++) {
		Player *pl = dynamic_cast<Player*>(*it);
		if (!pl) continue;
		KeymapController *c = dynamic_cast<KeymapController*>(pl->getController());
		if (c) {
			int ojoyid = -1;
			if (c->isConcerned(e, ojoyid)) return;
			continue;
		}
	}
	/* this key event is not owned by a player, have a look at controller templates */
	std::vector<KeymapController*> &cd = cdef.forplayer;
	for(unsigned i = 0; i < cd.size(); i++) {
		int ojoyid = -1;
		if (cd[i] && cd[i]->isConcerned(e, ojoyid)) {
			addPlayer(i, ojoyid);
			break;
		}
	}
}
Player *Engine::getPlayerByJoystickId(int joyid) {
	for(EntitiesIterator it=entities.begin(); it != entities.end(); it++) {
		Player *pl = dynamic_cast<Player*>(*it);
		if (!pl) continue;
		KeymapController *c = dynamic_cast<KeymapController*>(pl->getController());
		if (c) {
			if (c->getJoystickId() == joyid) return pl;
			continue;
		}
		JoystickController *j = dynamic_cast<JoystickController*>(pl->getController());
		if (j) {
			if (j->getJoystickId() == joyid) return pl;
		}
	}
	return NULL;
}
void Engine::addPlayer(unsigned cid, int joyid) {
	if (cid >= cdef.forplayer.size()) return;
	if (cid == 0 && joyid ==  -1) {
		/* search a free joystick */
		for(unsigned i=0; i < Joystick::Count; i++) {
			if (Joystick::isConnected(i) && !getPlayerByJoystickId(i)) {
				joyid = i;
				break;
			}
		}
	}
	if (!cdef.forplayer[cid]) {
		fprintf(stderr, "Error: no controller %d found for new player\n", cid);
		return;
	}
	if (cid > 0) joyid = -1;
	add(new Player(cdef.forplayer[cid]->clone(joyid), this));
}
Entity *Engine::getMapBoundariesEntity() {
	return map_boundaries_entity;
}
TextureCache *Engine::getTextureCache(void) const {
	return &texture_cache;
}
Vector2d Engine::map_size(void) {
	Vector2u sz = window.getSize();
	return Vector2d(sz.x, sz.y);
}
Engine::EntitiesIterator Engine::begin_entities() {
	return entities.begin();
}
Engine::EntitiesIterator Engine::end_entities() {
	return entities.end();
}
Engine::Engine() {
	first_step = true;
	must_quit = false;
	window.create(VideoMode(1920,1080), "Tank window", Style::Default);
	window.setVerticalSyncEnabled(true);
	window.clear(Color::White);
	score_font.loadFromFile("/usr/share/fonts/truetype/droid/DroidSans.ttf");

	load_texture(background, background_texture, "sprites/dirt.jpg");
	Vector2d sz = map_size();
	background.setTextureRect(IntRect(0,0,sz.x,sz.y));
	map_boundaries_entity = new Wall(0,0,sz.x,sz.y, NULL, this);
	add(map_boundaries_entity);
	load_keymap(cdef, "keymap.json");
}
Engine::~Engine() {
	for(EntitiesIterator it=entities.begin(); it != entities.end(); ++it) {
		delete (*it);
	}
	window.close();
}
void Engine::seekCollisions(Entity *entity) {
	int i=100;
	bool retry = false;
	do {
	for(EntitiesIterator it=entities.begin(); it != entities.end(); ++it) {
		Entity *centity = (*it);
		Segment vect;
		vect.pt1 = entity->position;
		vect.pt2 = entity->position;
		MoveContext ctx(IT_GHOST, vect);
		if (interacts(this, ctx, entity, centity)) {
			CollisionEvent e;
			e.first = entity;
			e.second = centity;
			e.interaction = IT_GHOST;
			broadcast(&e);
			if (e.retry) retry = true;
		}
	}
	} while(retry && --i > 0);
}
void Engine::add(Entity *entity) {
	entity->setEngine(this);
	entities.push_back(entity);
	/* signal spawn-time collisions */
	seekCollisions(entity);
}
void Engine::destroy(Entity *entity) { /* Removes entity from engine and deletes the underlying object */
	entity->setKilled();
}
void Engine::broadcast(EngineEvent *event) {
	/* assume iterators may be invalidated during event processing, because new items may be added */
	/* note that entities may be set to killed, but cannot be removed from the list (they are only flagged) */

	for(size_t i=0; i < entities.size(); ++i) {
		Entity *entity = entities[i];
		if (!entity->isKilled()) entity->event_received(event);
	}
}
void Engine::quit(void) {
	must_quit = true;
}
bool Engine::step(void) {
	if (must_quit) return false;
	if (first_step) {clock.restart();first_step=false;}
	draw();
	compute_physics();
	destroy_flagged();
	return !must_quit;
}
void draw_score(RenderTarget &target, Font &ft, int score, Color color, Vector2d pos) {
	Text text;
	char sscore[256];
	sprintf(sscore, "%d", score);
	text.setCharacterSize(128);
	text.setString(sscore);
	text.setColor(color);
	text.setPosition(Vector2f(pos.x, pos.y));
	text.setFont(ft);

	target.draw(text);
}
void Engine::draw(void) {
	Vector2d scorepos;
	scorepos.x = 16;
	scorepos.y = 16;
	window.clear();
	window.draw(background);
	for(EntitiesIterator it=entities.begin(); it != entities.end(); ++it) {
		(*it)->draw(window);

		if (Player *pl = dynamic_cast<Player*>((*it))) {
			draw_score(window, score_font, pl->getScore(), pl->getColor(), scorepos);
			scorepos.x += 384+16;
		}
	}
	window.display();
}
static double getEntityRadius(Entity *a) {
	return a->getSize().x/2;
}
static DoubleRect getEntityBoundingRectangle(Entity *a) {
	Vector2d pos = a->position;
	Vector2d size = a->getSize();
	DoubleRect r;
	r.left = pos.x;
	r.top  = pos.y;
	r.width  = size.x;
	r.height = size.y;
	return r;
}
static Circle getEntityCircle(Entity *a) {
	Circle circle;
	circle.filled = true;
	circle.center = a->position;
	circle.radius = getEntityRadius(a);
	return circle;
}
/* Note: Dynamic entities must be circle-shaped */
static bool interacts(Engine *engine, MoveContext &ctx, Entity *a, Entity *b) {
	if (a->shape == SHAPE_CIRCLE && b->shape == SHAPE_RECTANGLE) {
		GeomRectangle gr;
		gr.filled = (b != engine->getMapBoundariesEntity());
		gr.r = getEntityBoundingRectangle(b);
		return moveCircleToRectangle(getEntityRadius(a), ctx, gr);
	} else if (a->shape == SHAPE_CIRCLE && b->shape == SHAPE_CIRCLE) {
		return moveCircleToCircle(getEntityRadius(a), ctx, getEntityCircle(b));
	} else {
		return false;
	}
}
bool moveCircleToRectangle(double radius, MoveContext &ctx, const DoubleRect &r);
bool moveCircleToCircle(double radius, MoveContext &ctx, const Circle &colli);

static bool quasi_equals(double a, double b) {
	return fabs(a-b) <= 1e-3*(fabs(a)+fabs(b));
}
void Engine::compute_physics(void) {
	Int64 tm = clock.getElapsedTime().asMicroseconds();
	if (tm == 0) return;
	clock.restart();
	for(EntitiesIterator it=entities.begin(); it != entities.end(); ++it) {
		Entity *entity = (*it);
		if (entity->isKilled()) continue;
		Vector2d movement = entity->movement(tm);
		Vector2d old_speed = movement;
		old_speed.x /= tm;
		old_speed.y /= tm;
		Segment vect;
		vect.pt1 = entity->position;
		vect.pt2 = vect.pt1 + movement;
		if ((fabs(movement.x)+fabs(movement.y)) < 1e-4) continue;
		MoveContext ctx(IT_GHOST, vect);
		for(int pass=0; pass < 2; ++pass)
		for (EntitiesIterator itc=entities.begin(); itc != entities.end(); ++itc) {
			Entity *centity = *itc;
			if (centity->isKilled()) continue;
			if (entity->isKilled()) break;
			if (centity == entity) continue;
			ctx.interaction = IT_GHOST;
			MoveContext ctxtemp = ctx;
			if (interacts(this, ctxtemp, entity, centity)) {
				if (entity->isKilled() || centity->isKilled()) continue;
				CollisionEvent e;
				e.type   = COLLIDE_EVENT;
				e.first  = entity;
				e.second = centity;
				e.interaction = IT_GHOST; /* default interaction type */
				broadcast(&e); /* should set e.interaction */
				ctx.interaction = e.interaction;
				if (pass == 1 && ctx.interaction != IT_GHOST) {
					/* On second interaction in the same frame, cancel movement */
					ctx.vect.pt2 = ctx.vect.pt1 = entity->position;
					break;
				}
				interacts(this, ctx, entity, centity);
			}
		}
		if (entity->isKilled()) continue;
		vect = ctx.vect;
		CompletedMovementEvent e;
		e.type = COMPLETED_MOVEMENT_EVENT;
		e.entity = entity;
		e.position = vect.pt2;
		Vector2d new_speed = ctx.nmove;
		new_speed.x /= tm;
		new_speed.y /= tm;
		if (quasi_equals(old_speed.x, new_speed.x) && quasi_equals(old_speed.y, new_speed.y)) {
			e.new_speed = old_speed;
			e.has_new_speed = false;
		} else {
			e.new_speed = new_speed;
			e.has_new_speed = true;
		}
		broadcast(&e);
	}
}
void Engine::destroy_flagged(void) {
	bool some_got_deleted;
	do {
		some_got_deleted = false;
		for(size_t i=0; i < entities.size(); ++i) {
			Entity *entity = entities[i];
			if (entity->isKilled()) {
				some_got_deleted = true;
				EntityDestroyedEvent e;
				e.type = ENTITY_DESTROYED_EVENT;
				e.entity = entity;
				broadcast(&e);
				entities.erase(entities.begin()+i);
				delete entity;
				--i;
			}
		}
	} while(some_got_deleted);
#if 0
	if (some_got_deleted) fprintf(stderr, "[now, I'm going to physically destroy things]\n");
	for(size_t i=0; i < entities.size(); ++i) {
		Entity *entity = entities[i];
		if (entity->isKilled()) {
			entities.erase(entities.begin()+i);
			delete entity;
			--i;
		}
	}
	if (some_got_deleted) fprintf(stderr, "[/physically destroyed things]\n");
#endif
}

<commit_msg>Fixed bug that ignored keypress events in controller spawning code.<commit_after>#include <math.h>

#include "engine.h"
#include "entity.h"
#include "geometry.h"
#include "engine_event.h"
#include "misc.h"
#include "wall.h"
#include "player.h"
#include <SFML/Window/VideoMode.hpp>
#include <SFML/Window/Window.hpp>
#include <SFML/Graphics/Text.hpp>
#include <stdio.h>
#include "commands.h"

using namespace sf;

static bool interacts(Engine *engine, MoveContext &ctx, Entity *a, Entity *b);

void Engine::play(void) {
	while (step()) {
		Event e;
		while (window.pollEvent(e)) {
			if (e.type == Event::Closed) {
				quit();
			} else if (e.type == Event::KeyPressed) {
				if (e.key.code == Keyboard::Escape) quit();
			} else if (e.type == Event::JoystickConnected) {
				addPlayer(0, e.joystickConnect.joystickId);
			} else if (e.type == Event::JoystickDisconnected) {
				Player *pl = getPlayerByJoystickId(e.joystickConnect.joystickId);
				if (pl) {destroy(pl);destroy_flagged();}
			}
			if (KeymapController::maybeConcerned(e)) {
				controller_activity(e);
			}
		}
	}
}

void Engine::controller_activity(Event &e) {
	for(EntitiesIterator it=entities.begin(); it != entities.end(); it++) {
		Player *pl = dynamic_cast<Player*>(*it);
		if (!pl) continue;
		KeymapController *c = dynamic_cast<KeymapController*>(pl->getController());
		if (c) {
			int ojoyid = -1;
			if (c->isConcerned(e, ojoyid)) return;
			continue;
		}
	}
	/* this key event is not owned by a player, have a look at controller templates */
	std::vector<KeymapController*> &cd = cdef.forplayer;
	for(unsigned i = 0; i < cd.size(); i++) {
		int ojoyid = -1;
		if (cd[i] && cd[i]->isConcerned(e, ojoyid)) {
			addPlayer(i, ojoyid);
			break;
		}
	}
}
Player *Engine::getPlayerByJoystickId(int joyid) {
	for(EntitiesIterator it=entities.begin(); it != entities.end(); it++) {
		Player *pl = dynamic_cast<Player*>(*it);
		if (!pl) continue;
		KeymapController *c = dynamic_cast<KeymapController*>(pl->getController());
		if (c) {
			if (c->getJoystickId() == joyid) return pl;
			continue;
		}
		JoystickController *j = dynamic_cast<JoystickController*>(pl->getController());
		if (j) {
			if (j->getJoystickId() == joyid) return pl;
		}
	}
	return NULL;
}
void Engine::addPlayer(unsigned cid, int joyid) {
	if (cid >= cdef.forplayer.size()) return;
	if (cid == 0 && joyid ==  -1) {
		/* search a free joystick */
		for(unsigned i=0; i < Joystick::Count; i++) {
			if (Joystick::isConnected(i) && !getPlayerByJoystickId(i)) {
				joyid = i;
				break;
			}
		}
	}
	if (!cdef.forplayer[cid]) {
		fprintf(stderr, "Error: no controller %d found for new player\n", cid);
		return;
	}
	if (cid > 0) joyid = -1;
	add(new Player(cdef.forplayer[cid]->clone(joyid), this));
}
Entity *Engine::getMapBoundariesEntity() {
	return map_boundaries_entity;
}
TextureCache *Engine::getTextureCache(void) const {
	return &texture_cache;
}
Vector2d Engine::map_size(void) {
	Vector2u sz = window.getSize();
	return Vector2d(sz.x, sz.y);
}
Engine::EntitiesIterator Engine::begin_entities() {
	return entities.begin();
}
Engine::EntitiesIterator Engine::end_entities() {
	return entities.end();
}
Engine::Engine() {
	first_step = true;
	must_quit = false;
	window.create(VideoMode(1920,1080), "Tank window", Style::Default);
	window.setVerticalSyncEnabled(true);
	window.clear(Color::White);
	score_font.loadFromFile("/usr/share/fonts/truetype/droid/DroidSans.ttf");

	load_texture(background, background_texture, "sprites/dirt.jpg");
	Vector2d sz = map_size();
	background.setTextureRect(IntRect(0,0,sz.x,sz.y));
	map_boundaries_entity = new Wall(0,0,sz.x,sz.y, NULL, this);
	add(map_boundaries_entity);
	load_keymap(cdef, "keymap.json");
}
Engine::~Engine() {
	for(EntitiesIterator it=entities.begin(); it != entities.end(); ++it) {
		delete (*it);
	}
	window.close();
}
void Engine::seekCollisions(Entity *entity) {
	int i=100;
	bool retry = false;
	do {
	for(EntitiesIterator it=entities.begin(); it != entities.end(); ++it) {
		Entity *centity = (*it);
		Segment vect;
		vect.pt1 = entity->position;
		vect.pt2 = entity->position;
		MoveContext ctx(IT_GHOST, vect);
		if (interacts(this, ctx, entity, centity)) {
			CollisionEvent e;
			e.first = entity;
			e.second = centity;
			e.interaction = IT_GHOST;
			broadcast(&e);
			if (e.retry) retry = true;
		}
	}
	} while(retry && --i > 0);
}
void Engine::add(Entity *entity) {
	entity->setEngine(this);
	entities.push_back(entity);
	/* signal spawn-time collisions */
	seekCollisions(entity);
}
void Engine::destroy(Entity *entity) { /* Removes entity from engine and deletes the underlying object */
	entity->setKilled();
}
void Engine::broadcast(EngineEvent *event) {
	/* assume iterators may be invalidated during event processing, because new items may be added */
	/* note that entities may be set to killed, but cannot be removed from the list (they are only flagged) */

	for(size_t i=0; i < entities.size(); ++i) {
		Entity *entity = entities[i];
		if (!entity->isKilled()) entity->event_received(event);
	}
}
void Engine::quit(void) {
	must_quit = true;
}
bool Engine::step(void) {
	if (must_quit) return false;
	if (first_step) {clock.restart();first_step=false;}
	draw();
	compute_physics();
	destroy_flagged();
	return !must_quit;
}
void draw_score(RenderTarget &target, Font &ft, int score, Color color, Vector2d pos) {
	Text text;
	char sscore[256];
	sprintf(sscore, "%d", score);
	text.setCharacterSize(128);
	text.setString(sscore);
	text.setColor(color);
	text.setPosition(Vector2f(pos.x, pos.y));
	text.setFont(ft);

	target.draw(text);
}
void Engine::draw(void) {
	Vector2d scorepos;
	scorepos.x = 16;
	scorepos.y = 16;
	window.clear();
	window.draw(background);
	for(EntitiesIterator it=entities.begin(); it != entities.end(); ++it) {
		(*it)->draw(window);

		if (Player *pl = dynamic_cast<Player*>((*it))) {
			draw_score(window, score_font, pl->getScore(), pl->getColor(), scorepos);
			scorepos.x += 384+16;
		}
	}
	window.display();
}
static double getEntityRadius(Entity *a) {
	return a->getSize().x/2;
}
static DoubleRect getEntityBoundingRectangle(Entity *a) {
	Vector2d pos = a->position;
	Vector2d size = a->getSize();
	DoubleRect r;
	r.left = pos.x;
	r.top  = pos.y;
	r.width  = size.x;
	r.height = size.y;
	return r;
}
static Circle getEntityCircle(Entity *a) {
	Circle circle;
	circle.filled = true;
	circle.center = a->position;
	circle.radius = getEntityRadius(a);
	return circle;
}
/* Note: Dynamic entities must be circle-shaped */
static bool interacts(Engine *engine, MoveContext &ctx, Entity *a, Entity *b) {
	if (a->shape == SHAPE_CIRCLE && b->shape == SHAPE_RECTANGLE) {
		GeomRectangle gr;
		gr.filled = (b != engine->getMapBoundariesEntity());
		gr.r = getEntityBoundingRectangle(b);
		return moveCircleToRectangle(getEntityRadius(a), ctx, gr);
	} else if (a->shape == SHAPE_CIRCLE && b->shape == SHAPE_CIRCLE) {
		return moveCircleToCircle(getEntityRadius(a), ctx, getEntityCircle(b));
	} else {
		return false;
	}
}
bool moveCircleToRectangle(double radius, MoveContext &ctx, const DoubleRect &r);
bool moveCircleToCircle(double radius, MoveContext &ctx, const Circle &colli);

static bool quasi_equals(double a, double b) {
	return fabs(a-b) <= 1e-3*(fabs(a)+fabs(b));
}
void Engine::compute_physics(void) {
	Int64 tm = clock.getElapsedTime().asMicroseconds();
	if (tm == 0) return;
	clock.restart();
	for(EntitiesIterator it=entities.begin(); it != entities.end(); ++it) {
		Entity *entity = (*it);
		if (entity->isKilled()) continue;
		Vector2d movement = entity->movement(tm);
		Vector2d old_speed = movement;
		old_speed.x /= tm;
		old_speed.y /= tm;
		Segment vect;
		vect.pt1 = entity->position;
		vect.pt2 = vect.pt1 + movement;
		if ((fabs(movement.x)+fabs(movement.y)) < 1e-4) continue;
		MoveContext ctx(IT_GHOST, vect);
		for(int pass=0; pass < 2; ++pass)
		for (EntitiesIterator itc=entities.begin(); itc != entities.end(); ++itc) {
			Entity *centity = *itc;
			if (centity->isKilled()) continue;
			if (entity->isKilled()) break;
			if (centity == entity) continue;
			ctx.interaction = IT_GHOST;
			MoveContext ctxtemp = ctx;
			if (interacts(this, ctxtemp, entity, centity)) {
				if (entity->isKilled() || centity->isKilled()) continue;
				CollisionEvent e;
				e.type   = COLLIDE_EVENT;
				e.first  = entity;
				e.second = centity;
				e.interaction = IT_GHOST; /* default interaction type */
				broadcast(&e); /* should set e.interaction */
				ctx.interaction = e.interaction;
				if (pass == 1 && ctx.interaction != IT_GHOST) {
					/* On second interaction in the same frame, cancel movement */
					ctx.vect.pt2 = ctx.vect.pt1 = entity->position;
					break;
				}
				interacts(this, ctx, entity, centity);
			}
		}
		if (entity->isKilled()) continue;
		vect = ctx.vect;
		CompletedMovementEvent e;
		e.type = COMPLETED_MOVEMENT_EVENT;
		e.entity = entity;
		e.position = vect.pt2;
		Vector2d new_speed = ctx.nmove;
		new_speed.x /= tm;
		new_speed.y /= tm;
		if (quasi_equals(old_speed.x, new_speed.x) && quasi_equals(old_speed.y, new_speed.y)) {
			e.new_speed = old_speed;
			e.has_new_speed = false;
		} else {
			e.new_speed = new_speed;
			e.has_new_speed = true;
		}
		broadcast(&e);
	}
}
void Engine::destroy_flagged(void) {
	bool some_got_deleted;
	do {
		some_got_deleted = false;
		for(size_t i=0; i < entities.size(); ++i) {
			Entity *entity = entities[i];
			if (entity->isKilled()) {
				some_got_deleted = true;
				EntityDestroyedEvent e;
				e.type = ENTITY_DESTROYED_EVENT;
				e.entity = entity;
				broadcast(&e);
				entities.erase(entities.begin()+i);
				delete entity;
				--i;
			}
		}
	} while(some_got_deleted);
#if 0
	if (some_got_deleted) fprintf(stderr, "[now, I'm going to physically destroy things]\n");
	for(size_t i=0; i < entities.size(); ++i) {
		Entity *entity = entities[i];
		if (entity->isKilled()) {
			entities.erase(entities.begin()+i);
			delete entity;
			--i;
		}
	}
	if (some_got_deleted) fprintf(stderr, "[/physically destroyed things]\n");
#endif
}

<|endoftext|>


<commit_before>// Regression test for
// https://bugs.llvm.org/show_bug.cgi?id=32434

// RUN: %clangxx_asan -fexceptions -O0 %s -o %t
// RUN: %run %t

#include <assert.h>
#include <exception>
#include <sanitizer/asan_interface.h>

namespace {

// Not instrumented because std::rethrow_exception is a [[noreturn]] function,
// for which the compiler would emit a call to __asan_handle_no_return which
// unpoisons the stack.
// We emulate here some code not compiled with asan. This function is not
// [[noreturn]] because the scenario we're emulating doesn't always throw. If it
// were [[noreturn]], the calling code would emit a call to
// __asan_handle_no_return.
void __attribute__((no_sanitize("address")))
uninstrumented_rethrow_exception(std::exception_ptr const &exc_ptr) {
  std::rethrow_exception(exc_ptr);
}

char *poisoned1;
char *poisoned2;

// Create redzones for stack variables in shadow memory and call
// std::rethrow_exception which should unpoison the entire stack.
void create_redzones_and_throw(std::exception_ptr const &exc_ptr) {
  char a[100];
  poisoned1 = a - 1;
  poisoned2 = a + sizeof(a);
  assert(__asan_address_is_poisoned(poisoned1));
  assert(__asan_address_is_poisoned(poisoned2));
  uninstrumented_rethrow_exception(exc_ptr);
}

} // namespace

// Check that std::rethrow_exception is intercepted by asan and the interception
// unpoisons the stack.
// If std::rethrow_exception is NOT intercepted, then calls to this function
// from instrumented code will still unpoison the stack because
// std::rethrow_exception is a [[noreturn]] function and any [[noreturn]]
// function call will be instrumented with __asan_handle_no_return.
// However, calls to std::rethrow_exception from UNinstrumented code will not
// unpoison the stack, so we need to intercept std::rethrow_exception to
// unpoison the stack.
int main() {
  // In some implementations of std::make_exception_ptr, e.g. libstdc++ prior to
  // gcc 7, this function calls __cxa_throw. The __cxa_throw is intercepted by
  // asan to unpoison the entire stack; since this test essentially tests that
  // the stack is unpoisoned by a call to std::rethrow_exception, we need to
  // generate the exception_ptr BEFORE we have the local variables poison the
  // stack.
  std::exception_ptr my_exception_ptr = std::make_exception_ptr("up");

  try {
    create_redzones_and_throw(my_exception_ptr);
  } catch(char const *) {
    assert(!__asan_region_is_poisoned(poisoned1, poisoned2 - poisoned1 + 1));
  }
}
<commit_msg>Mark intercept-rethrow-exception.cc as XFAIL on NetBSD<commit_after>// Regression test for
// https://bugs.llvm.org/show_bug.cgi?id=32434

// RUN: %clangxx_asan -fexceptions -O0 %s -o %t
// RUN: %run %t

// The current implementation of this functionality requires special
// combination of libraries that are not used by default on NetBSD
// XFAIL: netbsd

#include <assert.h>
#include <exception>
#include <sanitizer/asan_interface.h>

namespace {

// Not instrumented because std::rethrow_exception is a [[noreturn]] function,
// for which the compiler would emit a call to __asan_handle_no_return which
// unpoisons the stack.
// We emulate here some code not compiled with asan. This function is not
// [[noreturn]] because the scenario we're emulating doesn't always throw. If it
// were [[noreturn]], the calling code would emit a call to
// __asan_handle_no_return.
void __attribute__((no_sanitize("address")))
uninstrumented_rethrow_exception(std::exception_ptr const &exc_ptr) {
  std::rethrow_exception(exc_ptr);
}

char *poisoned1;
char *poisoned2;

// Create redzones for stack variables in shadow memory and call
// std::rethrow_exception which should unpoison the entire stack.
void create_redzones_and_throw(std::exception_ptr const &exc_ptr) {
  char a[100];
  poisoned1 = a - 1;
  poisoned2 = a + sizeof(a);
  assert(__asan_address_is_poisoned(poisoned1));
  assert(__asan_address_is_poisoned(poisoned2));
  uninstrumented_rethrow_exception(exc_ptr);
}

} // namespace

// Check that std::rethrow_exception is intercepted by asan and the interception
// unpoisons the stack.
// If std::rethrow_exception is NOT intercepted, then calls to this function
// from instrumented code will still unpoison the stack because
// std::rethrow_exception is a [[noreturn]] function and any [[noreturn]]
// function call will be instrumented with __asan_handle_no_return.
// However, calls to std::rethrow_exception from UNinstrumented code will not
// unpoison the stack, so we need to intercept std::rethrow_exception to
// unpoison the stack.
int main() {
  // In some implementations of std::make_exception_ptr, e.g. libstdc++ prior to
  // gcc 7, this function calls __cxa_throw. The __cxa_throw is intercepted by
  // asan to unpoison the entire stack; since this test essentially tests that
  // the stack is unpoisoned by a call to std::rethrow_exception, we need to
  // generate the exception_ptr BEFORE we have the local variables poison the
  // stack.
  std::exception_ptr my_exception_ptr = std::make_exception_ptr("up");

  try {
    create_redzones_and_throw(my_exception_ptr);
  } catch(char const *) {
    assert(!__asan_region_is_poisoned(poisoned1, poisoned2 - poisoned1 + 1));
  }
}
<|endoftext|>