<commit_before>#include <unistd.h>
#include <signal.h>

#include "acmacs-base/argv.hh"
#include "acmacs-base/temp-file.hh"
#include "acmacs-base/string-compare.hh"
#include "acmacs-base/string-split.hh"
#include "acmacs-base/quicklook.hh"
#include "acmacs-base/filesystem.hh"
#include "acmacs-map-draw/setup-dbs.hh"
#include "acmacs-map-draw/log.hh"
#include "acmacs-map-draw/mapi-settings.hh"
#include "acmacs-map-draw/draw.hh"

// ----------------------------------------------------------------------

constexpr const std::string_view sHelpPost = R"(
one input chart: make map
two input charts: make procrustes
output /: do not generate any output
no output: generate temp pdf and open it (and then delete it)

-D <arg> --define <arg>

)";

using namespace acmacs::argv;
struct MapiOptions : public acmacs::argv::v2::argv
{
    MapiOptions(int a_argc, const char* const a_argv[], on_error on_err = on_error::exit) { parse(a_argc, a_argv, on_err); }
    // std::string_view help_pre() const override { return sHelpPost; }
    std::string_view help_post() const override { return sHelpPost; }

    option<str> db_dir{*this, "db-dir"};
    // option<str> seqdb{*this, "seqdb"};

    option<str_array> settings_files{*this, 's'};
    option<str_array> defines{*this, 'D', "define", desc{"see {ACMACSD_ROOT}/share/doc/mapi.org"}};
    option<str_array> apply{*this, 'a', "apply", desc{"comma separated names or json array to use as \"apply\", e.g. [\"/all-grey\",\"/egg\",\"/clades\",\"/labels\"]"}};
    option<bool>      interactive{*this, 'i', "interactive"};
    // option<str>       previous{*this, "previous"};
    option<size_t>    projection{*this, 'p', "projection", dflt{0ul}};
    option<size_t>    secondary_projection{*this, 'r', "secondary-projection", dflt{static_cast<size_t>(-1)}};

    option<bool>      open{*this, "open"};
    option<str>       preview{*this, "preview"};
    // option<bool>      ql{*this, "ql"};
    option<str_array> verbose{*this, 'v', "verbose", desc{"comma separated list (or multiple switches) of enablers"}};

    argument<str_array> files{*this, arg_name{"input: chart.ace, chart.save, chart.acd1; output: map.pdf, /"}, mandatory};
};

    // option<str>       apply_from{*this, "apply-from", desc{"read json array to use as \"apply\" from file (or stdin if \"-\""}};
    // option<bool>      clade{*this, "clade"};
    // option<double>    point_scale{*this, "point-scale", dflt{1.0}};
    // option<double>    rotate_degrees{*this, 'r', "rotate-degrees", dflt{0.0}, desc{"counter clockwise"}};
    // option<bool>      flip_ew{*this, "flip-ew"};
    // option<bool>      flip_ns{*this, "flip-ns"};
    // option<str>       save{*this, "save", desc{"save resulting chart with modified projection and plot spec"}};

// ----------------------------------------------------------------------

static std::pair<std::vector<std::string_view>, std::vector<std::string_view>> parse_files(const argument<str_array>& files);
static void signal_handler(int sig_num);

int main(int argc, char* const argv[])
{
    using namespace std::string_view_literals;
    int exit_code = 0;
    try {
        acmacs::log::register_enabler_acmacs_map_draw();
        MapiOptions opt(argc, argv);
        acmacs::log::enable(opt.verbose);
        setup_dbs(opt.db_dir, !opt.verbose.empty());

        const auto [inputs, outputs] = parse_files(opt.files);

        ChartDraw chart_draw{inputs, opt.projection};

        acmacs::mapi::Settings settings{chart_draw};
        settings.load_from_conf({"mapi.json"sv, "clades.json"sv, "vaccines.json"sv});
        settings.load(opt.settings_files);
        settings.set_defines(opt.defines);
        for (size_t chart_no = 0; chart_no < chart_draw.number_of_charts(); ++chart_no)
            settings.setenv_toplevel(fmt::format("chart[{}]", chart_no), chart_draw.chart(chart_no).filename());

        if (opt.interactive)
            signal(SIGHUP, signal_handler);

        bool opened{false};
        for (;;) {
            exit_code = 0;
            try {
                if (!opt.apply.empty()) {
                    for (const auto& to_apply : opt.apply) {
                        if (!to_apply.empty()) {
                            if (to_apply[0] == '{' || to_apply[0] == '[') {
                                settings.apply(to_apply);
                            }
                            else {
                                for (const auto& to_apply_one : acmacs::string::split(to_apply))
                                    settings.apply(to_apply_one);
                            }
                        }
                    }
                }
                else {
                    settings.apply("mapi"sv);
                }

                chart_draw.calculate_viewport();
                AD_INFO("{:.2f}", chart_draw.viewport("mapi main"));
                AD_INFO("transformation: {}", chart_draw.chart(0).modified_transformation());

                if (outputs.empty()) {
                    acmacs::file::temp output{fmt::format("{}--p{}.pdf", fs::path(inputs[0]).stem(), opt.projection)};
                    chart_draw.draw(output, 800, report_time::yes);
                    acmacs::quicklook(output, 2);
                }
                else if (outputs[0] == "/dev/null"sv || outputs[0] == "/"sv) { // do not generate pdf
                }
                else {
                    chart_draw.draw(outputs[0], 800, report_time::yes);
                    if ((opt.open || opt.interactive) && (!opt.preview || !opened))
                        acmacs::open(outputs[0]);
                    else if (opt.preview) {
                        acmacs::preview(outputs[0], opt.preview);
                        opened = true;
                    }
                }
            }
            catch (std::exception& err) {
                AD_ERROR("{}", err);
                acmacs::run_and_detach({"submarine"}, 0);
                exit_code = 1;
            }
            if (opt.interactive) {
                fmt::print(stderr, "mapi-i >> ");

                fd_set set;
                FD_ZERO(&set);
                FD_SET(0, &set);
                if (select(FD_SETSIZE, &set, nullptr, nullptr, nullptr) > 0) {
                    // fmt::print(stderr, " (reading)\n");
                    std::string input(20, ' ');
                    if (const auto bytes = ::read(0, input.data(), input.size()); bytes > 0) {
                        input.resize(static_cast<size_t>(bytes));
                        // fmt::print(stderr, " (read {} bytes)\n", bytes);
                    }
                }

                acmacs::run_and_detach({"tink"}, 0);
                fmt::print(stderr, "\n> ======================================================================================================================================================\n\n");
            }
            else
                break;

            try {
                chart_draw.reset();
                settings.reload();
            }
            catch (std::exception& err) {
                AD_ERROR("{}", err);
                acmacs::run_and_detach({"submarine"}, 0);
            }
        }
    }
    catch (std::exception& err) {
        fmt::print(stderr, "ERROR: {}\n", err);
        exit_code = 2;
    }
    return exit_code;
}

// ----------------------------------------------------------------------

std::pair<std::vector<std::string_view>, std::vector<std::string_view>> parse_files(const argument<str_array>& files)
{
    using namespace std::string_view_literals;
    std::vector<std::string_view> inputs, outputs;
    constexpr std::array input_suffixes{".ace"sv, ".acd1"sv, ".acd1.xz"sv, ".acd1.bz2"sv, ".save"sv, ".save.xz"sv, ".save.bz2"sv};
    const auto is_input = [&input_suffixes](std::string_view name) -> bool {
        return std::any_of(std::begin(input_suffixes), std::end(input_suffixes), [name](std::string_view suff) -> bool { return acmacs::string::endswith(name, suff); });
    };
    for (const auto& file : files) {
        if (is_input(file))
            inputs.push_back(file);
        else
            outputs.push_back(file);
    }
    if (inputs.empty())
        throw std::runtime_error{"no input files (charts) found in the command line"};
    if (inputs.size() > 2)
        throw std::runtime_error{fmt::format("too many input files () found in the command line", inputs)};
    return {inputs, outputs};

} // parse_files

// ----------------------------------------------------------------------

void signal_handler(int sig_num)
{
    fmt::print("SIGNAL {}\n", sig_num);

} // signal_handler

// ----------------------------------------------------------------------

// ----------------------------------------------------------------------
/// Local Variables:
/// eval: (if (fboundp 'eu-rename-buffer) (eu-rename-buffer))
/// End:
<commit_msg>minor fix<commit_after>#include <unistd.h>
#include <signal.h>

#include "acmacs-base/argv.hh"
#include "acmacs-base/temp-file.hh"
#include "acmacs-base/string-compare.hh"
#include "acmacs-base/string-split.hh"
#include "acmacs-base/quicklook.hh"
#include "acmacs-base/filesystem.hh"
#include "acmacs-map-draw/setup-dbs.hh"
#include "acmacs-map-draw/log.hh"
#include "acmacs-map-draw/mapi-settings.hh"
#include "acmacs-map-draw/draw.hh"

// ----------------------------------------------------------------------

constexpr const std::string_view sHelpPost = R"(
one input chart: make map
two input charts: make procrustes
output /: do not generate any output
no output: generate temp pdf and open it (and then delete it)

-D <arg> --define <arg>

)";

using namespace acmacs::argv;
struct MapiOptions : public acmacs::argv::v2::argv
{
    MapiOptions(int a_argc, const char* const a_argv[], on_error on_err = on_error::exit) { parse(a_argc, a_argv, on_err); }
    // std::string_view help_pre() const override { return sHelpPost; }
    std::string_view help_post() const override { return sHelpPost; }

    option<str> db_dir{*this, "db-dir"};
    // option<str> seqdb{*this, "seqdb"};

    option<str_array> settings_files{*this, 's', desc{"settings"}};
    option<str_array> defines{*this, 'D', "define", desc{"see {ACMACSD_ROOT}/share/doc/mapi.org"}};
    option<str_array> apply{*this, 'a', "apply", desc{"comma separated names or json array to use as \"apply\", e.g. [\"/all-grey\",\"/egg\",\"/clades\",\"/labels\"]"}};
    option<bool>      interactive{*this, 'i', "interactive"};
    // option<str>       previous{*this, "previous"};
    option<size_t>    projection{*this, 'p', "projection", dflt{0ul}};
    option<size_t>    secondary_projection{*this, 'r', "secondary-projection", dflt{static_cast<size_t>(-1)}};

    option<bool>      open{*this, "open"};
    option<str>       preview{*this, "preview"};
    // option<bool>      ql{*this, "ql"};
    option<str_array> verbose{*this, 'v', "verbose", desc{"comma separated list (or multiple switches) of enablers"}};

    argument<str_array> files{*this, arg_name{"input: chart.ace, chart.save, chart.acd1; output: map.pdf, /"}, mandatory};
};

    // option<str>       apply_from{*this, "apply-from", desc{"read json array to use as \"apply\" from file (or stdin if \"-\""}};
    // option<bool>      clade{*this, "clade"};
    // option<double>    point_scale{*this, "point-scale", dflt{1.0}};
    // option<double>    rotate_degrees{*this, 'r', "rotate-degrees", dflt{0.0}, desc{"counter clockwise"}};
    // option<bool>      flip_ew{*this, "flip-ew"};
    // option<bool>      flip_ns{*this, "flip-ns"};
    // option<str>       save{*this, "save", desc{"save resulting chart with modified projection and plot spec"}};

// ----------------------------------------------------------------------

static std::pair<std::vector<std::string_view>, std::vector<std::string_view>> parse_files(const argument<str_array>& files);
static void signal_handler(int sig_num);

int main(int argc, char* const argv[])
{
    using namespace std::string_view_literals;
    int exit_code = 0;
    try {
        acmacs::log::register_enabler_acmacs_map_draw();
        MapiOptions opt(argc, argv);
        acmacs::log::enable(opt.verbose);
        setup_dbs(opt.db_dir, !opt.verbose.empty());

        const auto [inputs, outputs] = parse_files(opt.files);

        ChartDraw chart_draw{inputs, opt.projection};

        acmacs::mapi::Settings settings{chart_draw};
        settings.load_from_conf({"mapi.json"sv, "clades.json"sv, "vaccines.json"sv});
        settings.load(opt.settings_files);
        settings.set_defines(opt.defines);
        for (size_t chart_no = 0; chart_no < chart_draw.number_of_charts(); ++chart_no)
            settings.setenv_toplevel(fmt::format("chart[{}]", chart_no), chart_draw.chart(chart_no).filename());

        if (opt.interactive)
            signal(SIGHUP, signal_handler);

        bool opened{false};
        for (;;) {
            exit_code = 0;
            try {
                if (!opt.apply.empty()) {
                    for (const auto& to_apply : opt.apply) {
                        if (!to_apply.empty()) {
                            if (to_apply[0] == '{' || to_apply[0] == '[') {
                                settings.apply(to_apply);
                            }
                            else {
                                for (const auto& to_apply_one : acmacs::string::split(to_apply))
                                    settings.apply(to_apply_one);
                            }
                        }
                    }
                }
                else {
                    settings.apply("mapi"sv);
                }

                chart_draw.calculate_viewport();
                AD_INFO("{:.2f}", chart_draw.viewport("mapi main"));
                AD_INFO("transformation: {}", chart_draw.chart(0).modified_transformation());

                if (outputs.empty()) {
                    acmacs::file::temp output{fmt::format("{}--p{}.pdf", fs::path(inputs[0]).stem(), opt.projection)};
                    chart_draw.draw(output, 800, report_time::yes);
                    acmacs::quicklook(output, 2);
                }
                else if (outputs[0] == "/dev/null"sv || outputs[0] == "/"sv) { // do not generate pdf
                }
                else {
                    chart_draw.draw(outputs[0], 800, report_time::yes);
                    if ((opt.open || opt.interactive) && (!opt.preview || !opened))
                        acmacs::open(outputs[0]);
                    else if (opt.preview) {
                        acmacs::preview(outputs[0], opt.preview);
                        opened = true;
                    }
                }
            }
            catch (std::exception& err) {
                AD_ERROR("{}", err);
                acmacs::run_and_detach({"submarine"}, 0);
                exit_code = 1;
            }
            if (opt.interactive) {
                fmt::print(stderr, "mapi-i >> ");

                fd_set set;
                FD_ZERO(&set);
                FD_SET(0, &set);
                if (select(FD_SETSIZE, &set, nullptr, nullptr, nullptr) > 0) {
                    // fmt::print(stderr, " (reading)\n");
                    std::string input(20, ' ');
                    if (const auto bytes = ::read(0, input.data(), input.size()); bytes > 0) {
                        input.resize(static_cast<size_t>(bytes));
                        // fmt::print(stderr, " (read {} bytes)\n", bytes);
                    }
                }

                acmacs::run_and_detach({"tink"}, 0);
                fmt::print(stderr, "\n> ======================================================================================================================================================\n\n");
            }
            else
                break;

            try {
                chart_draw.reset();
                settings.reload();
            }
            catch (std::exception& err) {
                AD_ERROR("{}", err);
                acmacs::run_and_detach({"submarine"}, 0);
            }
        }
    }
    catch (std::exception& err) {
        fmt::print(stderr, "ERROR: {}\n", err);
        exit_code = 2;
    }
    return exit_code;
}

// ----------------------------------------------------------------------

std::pair<std::vector<std::string_view>, std::vector<std::string_view>> parse_files(const argument<str_array>& files)
{
    using namespace std::string_view_literals;
    std::vector<std::string_view> inputs, outputs;
    constexpr std::array input_suffixes{".ace"sv, ".acd1"sv, ".acd1.xz"sv, ".acd1.bz2"sv, ".save"sv, ".save.xz"sv, ".save.bz2"sv};
    const auto is_input = [&input_suffixes](std::string_view name) -> bool {
        return std::any_of(std::begin(input_suffixes), std::end(input_suffixes), [name](std::string_view suff) -> bool { return acmacs::string::endswith(name, suff); });
    };
    for (const auto& file : files) {
        if (is_input(file))
            inputs.push_back(file);
        else
            outputs.push_back(file);
    }
    if (inputs.empty())
        throw std::runtime_error{"no input files (charts) found in the command line"};
    if (inputs.size() > 2)
        throw std::runtime_error{fmt::format("too many input files () found in the command line", inputs)};
    return {inputs, outputs};

} // parse_files

// ----------------------------------------------------------------------

void signal_handler(int sig_num)
{
    fmt::print("SIGNAL {}\n", sig_num);

} // signal_handler

// ----------------------------------------------------------------------

// ----------------------------------------------------------------------
/// Local Variables:
/// eval: (if (fboundp 'eu-rename-buffer) (eu-rename-buffer))
/// End:
<|endoftext|>


<commit_before>// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <time.h>
#include <signal.h>

#include <sys/select.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/file.h>
#include <fcntl.h>

#include <vespa/defaults.h>
#include <vespa/log/llparser.h>
#include "llreader.h"
#include <vespa/log/log.h>

LOG_SETUP("runserver");

// XXX should close all file descriptors,
// XXX but that makes logging go haywire

static volatile sig_atomic_t gotstopsig = 0;
static volatile sig_atomic_t lastsig = 0;
static volatile sig_atomic_t unhandledsig = 0;

extern "C" {
void termsig(int sig) {
    lastsig = sig;
    gotstopsig = 1;
    unhandledsig = 1;
}
}

class PidFile
{
private:
    char *_pidfile;
    int _fd;
    PidFile(const PidFile&);
    PidFile& operator= (const PidFile&);
public:
    PidFile(const char *pidfile) : _pidfile(strdup(pidfile)), _fd(-1) {}
    ~PidFile() { free(_pidfile); if (_fd >= 0) close(_fd); }
    int readPid();
    void writePid();
    bool writeOpen();
    bool isRunning();
    bool isMine();
    void cleanUp();
};

void
PidFile::cleanUp()
{
    if (isMine() || !isRunning()) remove(_pidfile);
    if (_fd >= 0) close(_fd);
    _fd = -1;
}

bool
PidFile::writeOpen()
{
    if (_fd >= 0) close(_fd);
    int flags = O_CREAT | O_WRONLY | O_NONBLOCK;
    _fd = open(_pidfile, flags, 0644);
    if (_fd < 0) {
        fprintf(stderr, "could not create pidfile %s: %s\n", _pidfile,
                strerror(errno));
        return false;
    }
    // XXX should we use locking or not?
    if (flock(_fd, LOCK_EX | LOCK_NB) != 0) {
        fprintf(stderr, "could not lock pidfile %s: %s\n", _pidfile,
                strerror(errno));
        close(_fd);
        _fd = -1;
        return false;
    }
    fcntl(_fd, F_SETFD, FD_CLOEXEC);
    return true;
}

void
PidFile::writePid()
{
    if (_fd < 0) abort();
    ftruncate(_fd, (off_t)0);
    char buf[100];
    sprintf(buf, "%d\n", getpid());
    int l = strlen(buf);
    ssize_t didw = write(_fd, buf, l);
    if (didw != l) {
        fprintf(stderr, "could not write pid to %s: %s\n",
                _pidfile, strerror(errno));
        exit(1);
    }
    LOG(debug, "wrote '%s' to %s (fd %d)", buf, _pidfile, _fd);
}

int
PidFile::readPid()
{
    FILE *pf = fopen(_pidfile, "r");
    if (pf == NULL) return 0;
    char buf[100];
    strcpy(buf, "0");
    fgets(buf, 100, pf);
    fclose(pf);
    return atoi(buf);
}

bool
PidFile::isRunning()
{
    int pid = readPid();
    if (pid < 1) return false;
    return (kill(pid, 0) == 0 || errno == EPERM);
}

bool
PidFile::isMine()
{
    int pid = readPid();
    return (pid == getpid());
}

using namespace ns_log;

int loop(const char *svc, char * const * run)
{
    int pstdout[2];
    int pstderr[2];

    if (pipe(pstdout) < 0 || pipe(pstderr) < 0) {
        LOG(error, "pipe: %s", strerror(errno));
        exit(1);
    }
    LOG(debug, "stdout pipe %d <- %d; stderr pipe %d <- %d",
        pstdout[0], pstdout[1],
        pstderr[0], pstderr[1]);

    int high = 1 + pstdout[0] + pstderr[0];

    pid_t child = fork();

    if (child == 0) {
        // I am the child process
        dup2(pstdout[1], 1);
        dup2(pstderr[1], 2);
        close(pstdout[0]);
        close(pstderr[0]);
        close(pstdout[1]);
        close(pstderr[1]);
        execvp(run[0], run);
        LOG(error, "exec %s: %s", run[0], strerror(errno));
        exit(1);
    }
    if (child < 0) {
        LOG(error, "fork(): %s", strerror(errno));
        exit(1);
    }
    // I am the parent process

    LOG(debug, "started %s (pid %d)", run[0], (int)child);
    std::string torun = run[0];
    for (char * const *arg = (run + 1); *arg != NULL; ++arg) {
        torun += " ";
        torun += *arg;
    }

    {
        torun += " (pid ";
        char buf[20];
        sprintf(buf, "%d", (int)child);
        torun += buf;
        torun += ")";
    }
    EV_STARTING(torun.c_str());

    close(pstdout[1]);
    close(pstderr[1]);

    LLParser outvia;
    LLParser errvia;
    outvia.setDefaultLevel(Logger::info);
    errvia.setDefaultLevel(Logger::warning);

    outvia.setService(svc);
    errvia.setService(svc);
    outvia.setComponent("stdout");
    errvia.setComponent("stderr");
    outvia.setPid(child);
    errvia.setPid(child);

    InputBuf outReader(pstdout[0]);
    InputBuf errReader(pstderr[0]);

    bool outeof = false;
    bool erreof = false;

    int wstat = 0;

    while (child || !outeof || !erreof) {
        struct timeval timeout;

        timeout.tv_sec = 0;
        timeout.tv_usec = 100000; // == 100 ms == 1/10 s

        fd_set pipes;

        FD_ZERO(&pipes);
        if (!outeof) FD_SET(pstdout[0], &pipes);
        if (!erreof) FD_SET(pstderr[0], &pipes);

        int n = select(high, &pipes, NULL, NULL, &timeout);
        if (n > 0) {
            if (FD_ISSET(pstdout[0], &pipes)) {
                LOG(debug, "out reader has input");
                if (outReader.blockRead()) {
                    while (outReader.hasInput()) {
                        LOG(debug, "process out reader input");
                        outReader.doInput(outvia);
                    }
                } else {
                    LOG(debug, "eof on stdout");
                    outeof = true; // EOF on stdout
                    close(pstdout[0]);
                }
            }
            if (FD_ISSET(pstderr[0], &pipes)) {
                LOG(debug, "err reader has input");
                if (errReader.blockRead()) {
                    while (errReader.hasInput()) {
                        LOG(debug, "process err reader input");
                        errReader.doInput(errvia);
                    }
                } else {
                    LOG(debug, "eof on stderr");
                    erreof = true; // EOF on stderr
                    close(pstderr[0]);
                }
            }
        }

        if (child != 0) {
            int cpid = waitpid(child, &wstat, WNOHANG);
            if (cpid == child) {
                if (WIFSTOPPED(wstat)) {
                    LOG(info, "child %d stopped, waiting for it to continue",
                        cpid);
                } else if (WIFEXITED(wstat)) {
                    // child terminated
                    LOG(debug, "child %d exit status: %d", cpid,
                        (int)WEXITSTATUS(wstat));
                    EV_STOPPED(torun.c_str(), (int)child, (int)WEXITSTATUS(wstat));
                    child = 0;
                } else if (WIFSIGNALED(wstat)) {
                    if (WTERMSIG(wstat) != lastsig) {
                        LOG(warning, "child died from signal: %d", WTERMSIG(wstat));
                        if (WCOREDUMP(wstat)) {
                            LOG(info, "child %d dumped core", cpid);
                        }
                    }
                    child = 0;
                } else {
                    LOG(error, "unexpected status %d from waidpit", wstat);
                    abort();
                }
            } else if (cpid < 0) {
                LOG(error, "waitpid: %s", strerror(errno));
                abort();
            } else if (cpid != 0) {
                LOG(warning, "unexpected status %d for pid %d",
                    wstat, cpid);
                abort();
            }
        }
        if (unhandledsig && child != 0) {
            LOG(debug, "got signal %d, sending to pid %d",
                (int)lastsig, (int)child);
            char why[256];
            sprintf(why, "got signal %d", (int)lastsig);
            EV_STOPPING(torun.c_str(), why);
            kill(child, lastsig);
            unhandledsig = 0;
        }
    }
    if (WIFSIGNALED(wstat))
        return WTERMSIG(wstat);
    return WEXITSTATUS(wstat);
}

void usage(char *prog, int es)
{
    fprintf(stderr, "Usage: %s\n"
            "       [-s service] [-r restartinterval] [-p pidfile]"
            " program [args ...]\n"
            "or:    [-p pidfile] [-k killcmd] -S\n", prog);
    exit(es);
}

int main(int argc, char *argv[])
{
    bool doStop = false;
    int restart = 0;
    const char *service = "runserver";
    const char *pidfile = "vespa-runserver.pid"; // XXX bad default?
    const char *killcmd = NULL;

    int ch;
    while ((ch = getopt(argc, argv, "k:s:r:p:Sh")) != -1) {
        switch (ch) {
        case 's':
            service = optarg;
            break;
        case 'r':
            restart = atoi(optarg);
            break;
        case 'p':
            pidfile = optarg;
            break;
        case 'S':
            doStop = true;
            break;
        case 'k':
            killcmd = optarg;
            break;
        default:
            usage(argv[0], ch != 'h');
        }
    }

    const char *envROOT = getenv("ROOT");
    if (envROOT == NULL || envROOT[0] == '\0') {
        envROOT = vespa::Defaults::vespaHome();
        setenv("ROOT", envROOT, 1);
    }
    if (chdir(envROOT) != 0) {
        fprintf(stderr, "Cannot chdir to %s: %s\n", envROOT, strerror(errno));
        exit(1);
    }

    PidFile mypf(pidfile);
    if (doStop) {
        if (mypf.isRunning()) {
            int pid = mypf.readPid();
            if (killcmd != NULL) {
                fprintf(stdout, "%s was running with pid %d, running '%s' to stop it\n",
                        service, pid, killcmd);
                if (system(killcmd) != 0) {
                    fprintf(stderr, "WARNING: stop command '%s' had some problem\n", killcmd);
                }
            } else {
                fprintf(stdout, "%s was running with pid %d, sending SIGTERM\n",
                    service, pid);
                if (killpg(pid, SIGTERM) != 0) {
                    fprintf(stderr, "could not signal %d: %s\n", pid,
                            strerror(errno));
                    exit(1);
                }
            }
            fprintf(stdout, "Waiting for exit (up to 60 seconds)\n");
            for (int cnt(0); cnt < 1800; cnt++) {
                usleep(100000); // wait 0.1 seconds
                if ((cnt > 300) && (cnt % 100 == 0)) {
                    killpg(pid, SIGTERM);
                }
                if (killpg(pid, 0) == 0) {
                    if (cnt%10 == 0) {
                        fprintf(stdout, ".");
                        fflush(stdout);
                    }
                } else {
                    fprintf(stdout, "DONE\n");
                    break;
                }
                if (cnt == 900) {
                    printf("\ngiving up, sending KILL signal\n");
                    killpg(pid, SIGKILL);
                }
            }
        } else {
            fprintf(stdout, "%s not running according to %s\n",
                    service, pidfile);
        }
        mypf.cleanUp();
        exit(0);
    }
    if (optind >= argc || killcmd != NULL) {
        usage(argv[0], 1);
    }

    if (mypf.isRunning()) {
        fprintf(stderr, "runserver already running with pid %d\n",
                mypf.readPid());
        exit(0);
    }

    if (!mypf.writeOpen()) {
        perror(pidfile);
        return 1;
    }

    pid_t rsp = fork();
    if (rsp == 0) {
        close(0);
        if (open("/dev/null", O_RDONLY) != 0) {
            perror("open /dev/null for reading failed");
            exit(1);
        }
        close(1);
        if (open("/dev/null", O_WRONLY) != 1) {
            perror("open /dev/null for writing failed");
            exit(1);
        }
        dup2(1, 2);
        if (setsid() < 0) {
            perror("setsid");
            exit(1);
        }
        struct sigaction act;
        struct sigaction oact;

        memset(&act, 0, sizeof(act));

        act.sa_handler = termsig;

        sigaction(SIGINT, &act, &oact);
        sigaction(SIGTERM, &act, &oact);

        int stat = 0;
        try {
            mypf.writePid();
            do {
                time_t laststart = time(NULL);
                stat = loop(service, argv+optind);
                if (restart > 0 && !gotstopsig) {
                    int wt = restart + laststart - time(NULL);
                    if (wt < 0) wt = 0;
                    LOG(info, "will restart in %d seconds", wt);
                }
                while (!gotstopsig && time(NULL) - laststart < restart) {
                    sleep(1);
                }
            } while (!gotstopsig && restart > 0);
        } catch (MsgException& ex) {
            LOG(error, "exception: '%s'", ex.what());
            exit(1);
        }
        if (restart > 0) {
            LOG(debug, "final exit status: %d", stat);
        }
        mypf.cleanUp();
        exit(stat);
    }

    if (rsp < 0) {
        perror("fork");
        return 1;
    }
    printf("runserver(%s) running with pid: %d\n", service, rsp);
    return 0;
}
<commit_msg>ignore SIGQUIT<commit_after>// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <time.h>
#include <signal.h>

#include <sys/select.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/file.h>
#include <fcntl.h>

#include <vespa/defaults.h>
#include <vespa/log/llparser.h>
#include "llreader.h"
#include <vespa/log/log.h>

LOG_SETUP("runserver");

// XXX should close all file descriptors,
// XXX but that makes logging go haywire

static volatile sig_atomic_t gotstopsig = 0;
static volatile sig_atomic_t lastsig = 0;
static volatile sig_atomic_t unhandledsig = 0;

extern "C" {
void termsig(int sig) {
    lastsig = sig;
    gotstopsig = 1;
    unhandledsig = 1;
}
}

class PidFile
{
private:
    char *_pidfile;
    int _fd;
    PidFile(const PidFile&);
    PidFile& operator= (const PidFile&);
public:
    PidFile(const char *pidfile) : _pidfile(strdup(pidfile)), _fd(-1) {}
    ~PidFile() { free(_pidfile); if (_fd >= 0) close(_fd); }
    int readPid();
    void writePid();
    bool writeOpen();
    bool isRunning();
    bool isMine();
    void cleanUp();
};

void
PidFile::cleanUp()
{
    if (isMine() || !isRunning()) remove(_pidfile);
    if (_fd >= 0) close(_fd);
    _fd = -1;
}

bool
PidFile::writeOpen()
{
    if (_fd >= 0) close(_fd);
    int flags = O_CREAT | O_WRONLY | O_NONBLOCK;
    _fd = open(_pidfile, flags, 0644);
    if (_fd < 0) {
        fprintf(stderr, "could not create pidfile %s: %s\n", _pidfile,
                strerror(errno));
        return false;
    }
    // XXX should we use locking or not?
    if (flock(_fd, LOCK_EX | LOCK_NB) != 0) {
        fprintf(stderr, "could not lock pidfile %s: %s\n", _pidfile,
                strerror(errno));
        close(_fd);
        _fd = -1;
        return false;
    }
    fcntl(_fd, F_SETFD, FD_CLOEXEC);
    return true;
}

void
PidFile::writePid()
{
    if (_fd < 0) abort();
    ftruncate(_fd, (off_t)0);
    char buf[100];
    sprintf(buf, "%d\n", getpid());
    int l = strlen(buf);
    ssize_t didw = write(_fd, buf, l);
    if (didw != l) {
        fprintf(stderr, "could not write pid to %s: %s\n",
                _pidfile, strerror(errno));
        exit(1);
    }
    LOG(debug, "wrote '%s' to %s (fd %d)", buf, _pidfile, _fd);
}

int
PidFile::readPid()
{
    FILE *pf = fopen(_pidfile, "r");
    if (pf == NULL) return 0;
    char buf[100];
    strcpy(buf, "0");
    fgets(buf, 100, pf);
    fclose(pf);
    return atoi(buf);
}

bool
PidFile::isRunning()
{
    int pid = readPid();
    if (pid < 1) return false;
    return (kill(pid, 0) == 0 || errno == EPERM);
}

bool
PidFile::isMine()
{
    int pid = readPid();
    return (pid == getpid());
}

using namespace ns_log;

int loop(const char *svc, char * const * run)
{
    int pstdout[2];
    int pstderr[2];

    if (pipe(pstdout) < 0 || pipe(pstderr) < 0) {
        LOG(error, "pipe: %s", strerror(errno));
        exit(1);
    }
    LOG(debug, "stdout pipe %d <- %d; stderr pipe %d <- %d",
        pstdout[0], pstdout[1],
        pstderr[0], pstderr[1]);

    int high = 1 + pstdout[0] + pstderr[0];

    pid_t child = fork();

    if (child == 0) {
        // I am the child process
        dup2(pstdout[1], 1);
        dup2(pstderr[1], 2);
        close(pstdout[0]);
        close(pstderr[0]);
        close(pstdout[1]);
        close(pstderr[1]);
        execvp(run[0], run);
        LOG(error, "exec %s: %s", run[0], strerror(errno));
        exit(1);
    }
    if (child < 0) {
        LOG(error, "fork(): %s", strerror(errno));
        exit(1);
    }
    // I am the parent process

    LOG(debug, "started %s (pid %d)", run[0], (int)child);
    std::string torun = run[0];
    for (char * const *arg = (run + 1); *arg != NULL; ++arg) {
        torun += " ";
        torun += *arg;
    }

    {
        torun += " (pid ";
        char buf[20];
        sprintf(buf, "%d", (int)child);
        torun += buf;
        torun += ")";
    }
    EV_STARTING(torun.c_str());

    close(pstdout[1]);
    close(pstderr[1]);

    LLParser outvia;
    LLParser errvia;
    outvia.setDefaultLevel(Logger::info);
    errvia.setDefaultLevel(Logger::warning);

    outvia.setService(svc);
    errvia.setService(svc);
    outvia.setComponent("stdout");
    errvia.setComponent("stderr");
    outvia.setPid(child);
    errvia.setPid(child);

    InputBuf outReader(pstdout[0]);
    InputBuf errReader(pstderr[0]);

    bool outeof = false;
    bool erreof = false;

    int wstat = 0;

    while (child || !outeof || !erreof) {
        struct timeval timeout;

        timeout.tv_sec = 0;
        timeout.tv_usec = 100000; // == 100 ms == 1/10 s

        fd_set pipes;

        FD_ZERO(&pipes);
        if (!outeof) FD_SET(pstdout[0], &pipes);
        if (!erreof) FD_SET(pstderr[0], &pipes);

        int n = select(high, &pipes, NULL, NULL, &timeout);
        if (n > 0) {
            if (FD_ISSET(pstdout[0], &pipes)) {
                LOG(debug, "out reader has input");
                if (outReader.blockRead()) {
                    while (outReader.hasInput()) {
                        LOG(debug, "process out reader input");
                        outReader.doInput(outvia);
                    }
                } else {
                    LOG(debug, "eof on stdout");
                    outeof = true; // EOF on stdout
                    close(pstdout[0]);
                }
            }
            if (FD_ISSET(pstderr[0], &pipes)) {
                LOG(debug, "err reader has input");
                if (errReader.blockRead()) {
                    while (errReader.hasInput()) {
                        LOG(debug, "process err reader input");
                        errReader.doInput(errvia);
                    }
                } else {
                    LOG(debug, "eof on stderr");
                    erreof = true; // EOF on stderr
                    close(pstderr[0]);
                }
            }
        }

        if (child != 0) {
            int cpid = waitpid(child, &wstat, WNOHANG);
            if (cpid == child) {
                if (WIFSTOPPED(wstat)) {
                    LOG(info, "child %d stopped, waiting for it to continue",
                        cpid);
                } else if (WIFEXITED(wstat)) {
                    // child terminated
                    LOG(debug, "child %d exit status: %d", cpid,
                        (int)WEXITSTATUS(wstat));
                    EV_STOPPED(torun.c_str(), (int)child, (int)WEXITSTATUS(wstat));
                    child = 0;
                } else if (WIFSIGNALED(wstat)) {
                    if (WTERMSIG(wstat) != lastsig) {
                        LOG(warning, "child died from signal: %d", WTERMSIG(wstat));
                        if (WCOREDUMP(wstat)) {
                            LOG(info, "child %d dumped core", cpid);
                        }
                    }
                    child = 0;
                } else {
                    LOG(error, "unexpected status %d from waidpit", wstat);
                    abort();
                }
            } else if (cpid < 0) {
                LOG(error, "waitpid: %s", strerror(errno));
                abort();
            } else if (cpid != 0) {
                LOG(warning, "unexpected status %d for pid %d",
                    wstat, cpid);
                abort();
            }
        }
        if (unhandledsig && child != 0) {
            LOG(debug, "got signal %d, sending to pid %d",
                (int)lastsig, (int)child);
            char why[256];
            sprintf(why, "got signal %d", (int)lastsig);
            EV_STOPPING(torun.c_str(), why);
            kill(child, lastsig);
            unhandledsig = 0;
        }
    }
    if (WIFSIGNALED(wstat))
        return WTERMSIG(wstat);
    return WEXITSTATUS(wstat);
}

void usage(char *prog, int es)
{
    fprintf(stderr, "Usage: %s\n"
            "       [-s service] [-r restartinterval] [-p pidfile]"
            " program [args ...]\n"
            "or:    [-p pidfile] [-k killcmd] -S\n", prog);
    exit(es);
}

int main(int argc, char *argv[])
{
    bool doStop = false;
    int restart = 0;
    const char *service = "runserver";
    const char *pidfile = "vespa-runserver.pid"; // XXX bad default?
    const char *killcmd = NULL;

    signal(SIGQUIT, SIG_IGN);

    int ch;
    while ((ch = getopt(argc, argv, "k:s:r:p:Sh")) != -1) {
        switch (ch) {
        case 's':
            service = optarg;
            break;
        case 'r':
            restart = atoi(optarg);
            break;
        case 'p':
            pidfile = optarg;
            break;
        case 'S':
            doStop = true;
            break;
        case 'k':
            killcmd = optarg;
            break;
        default:
            usage(argv[0], ch != 'h');
        }
    }

    const char *envROOT = getenv("ROOT");
    if (envROOT == NULL || envROOT[0] == '\0') {
        envROOT = vespa::Defaults::vespaHome();
        setenv("ROOT", envROOT, 1);
    }
    if (chdir(envROOT) != 0) {
        fprintf(stderr, "Cannot chdir to %s: %s\n", envROOT, strerror(errno));
        exit(1);
    }

    PidFile mypf(pidfile);
    if (doStop) {
        if (mypf.isRunning()) {
            int pid = mypf.readPid();
            if (killcmd != NULL) {
                fprintf(stdout, "%s was running with pid %d, running '%s' to stop it\n",
                        service, pid, killcmd);
                if (system(killcmd) != 0) {
                    fprintf(stderr, "WARNING: stop command '%s' had some problem\n", killcmd);
                }
            } else {
                fprintf(stdout, "%s was running with pid %d, sending SIGTERM\n",
                    service, pid);
                if (killpg(pid, SIGTERM) != 0) {
                    fprintf(stderr, "could not signal %d: %s\n", pid,
                            strerror(errno));
                    exit(1);
                }
            }
            fprintf(stdout, "Waiting for exit (up to 60 seconds)\n");
            for (int cnt(0); cnt < 1800; cnt++) {
                usleep(100000); // wait 0.1 seconds
                if ((cnt > 300) && (cnt % 100 == 0)) {
                    killpg(pid, SIGTERM);
                }
                if (killpg(pid, 0) == 0) {
                    if (cnt%10 == 0) {
                        fprintf(stdout, ".");
                        fflush(stdout);
                    }
                } else {
                    fprintf(stdout, "DONE\n");
                    break;
                }
                if (cnt == 900) {
                    printf("\ngiving up, sending KILL signal\n");
                    killpg(pid, SIGKILL);
                }
            }
        } else {
            fprintf(stdout, "%s not running according to %s\n",
                    service, pidfile);
        }
        mypf.cleanUp();
        exit(0);
    }
    if (optind >= argc || killcmd != NULL) {
        usage(argv[0], 1);
    }

    if (mypf.isRunning()) {
        fprintf(stderr, "runserver already running with pid %d\n",
                mypf.readPid());
        exit(0);
    }

    if (!mypf.writeOpen()) {
        perror(pidfile);
        return 1;
    }

    pid_t rsp = fork();
    if (rsp == 0) {
        close(0);
        if (open("/dev/null", O_RDONLY) != 0) {
            perror("open /dev/null for reading failed");
            exit(1);
        }
        close(1);
        if (open("/dev/null", O_WRONLY) != 1) {
            perror("open /dev/null for writing failed");
            exit(1);
        }
        dup2(1, 2);
        if (setsid() < 0) {
            perror("setsid");
            exit(1);
        }
        struct sigaction act;
        struct sigaction oact;

        memset(&act, 0, sizeof(act));

        act.sa_handler = termsig;

        sigaction(SIGINT, &act, &oact);
        sigaction(SIGTERM, &act, &oact);

        int stat = 0;
        try {
            mypf.writePid();
            do {
                time_t laststart = time(NULL);
                stat = loop(service, argv+optind);
                if (restart > 0 && !gotstopsig) {
                    int wt = restart + laststart - time(NULL);
                    if (wt < 0) wt = 0;
                    LOG(info, "will restart in %d seconds", wt);
                }
                while (!gotstopsig && time(NULL) - laststart < restart) {
                    sleep(1);
                }
            } while (!gotstopsig && restart > 0);
        } catch (MsgException& ex) {
            LOG(error, "exception: '%s'", ex.what());
            exit(1);
        }
        if (restart > 0) {
            LOG(debug, "final exit status: %d", stat);
        }
        mypf.cleanUp();
        exit(stat);
    }

    if (rsp < 0) {
        perror("fork");
        return 1;
    }
    printf("runserver(%s) running with pid: %d\n", service, rsp);
    return 0;
}
<|endoftext|>


<commit_before>#include <string.h>
#include <iostream>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Point_3.h>
#include <CGAL/Delaunay_triangulation_3.h>
#include "rply/rply.h"

using namespace std;
using namespace CGAL;

typedef Exact_predicates_inexact_constructions_kernel K;
typedef Point_3<K> Point;
typedef Delaunay_triangulation_3<K> Delaunay;

// PLC:
map <unsigned, Point> plcVertices; // mapping between vertex coordinates and corresponding unique id

class TriangleFace
{
	public:
		unsigned int vertexIds[3];
}tempFace;

class TetrahedronCell
{
	public:
		unsigned int vertexIds[4];
}tempTet;

list <TriangleFace> plcFaces; // contains ids of vertices/points making the triangle

// Delaunay tetrahedralization:
Delaunay DT;

// CDT:
map <unsigned, Point> cdtVertices;
list <TriangleFace> cdtTets; // contains ids of vertices making tetrahedron

static unsigned tempPoint[3];
unsigned int dimensionId = 0;
static unsigned pointId = 0;

static int vertex_cb(p_ply_argument argument)
{
	long eol;
	ply_get_argument_user_data(argument, NULL, &eol);
	tempPoint[dimensionId++] = ply_get_argument_value(argument);

	// insert the vertex into plcVertex
	if (eol)
	{
		plcVertices[pointId++] = Point(tempPoint[0],tempPoint[1],tempPoint[2]);
		dimensionId = 0;
	}
}

static int face_cb(p_ply_argument argument)
{
	long length, value_index;

	ply_get_argument_property(argument, NULL, &length, &value_index);

        switch (value_index)
	{
        	case 0:
	        case 1:
        		tempFace.vertexIds[pointId++] = ply_get_argument_value(argument);
	                break;
        	case 2:
			tempFace.vertexIds[pointId] = ply_get_argument_value(argument);
			pointId = 0;
			plcFaces.push_front(tempFace);
	                break;
        	default:
                	cout << "Invalid number of points in a face specified :(";
			break;
        }

}

// reads input PLC
void readPLCInput()
{
	// read PLY file(assumed to contain the PLC)
	string fileName;

	cout << "\nPlease enter input filename:\t";
	cin >> fileName;

	p_ply inputPLY = ply_open(fileName.c_str(), NULL, 0, NULL);

	if (!inputPLY) exit(0);

        if (!ply_read_header(inputPLY)) exit(0);

	if (!ply_read(inputPLY))
	{
		cout << "Cannot read the PLY file :(";
		exit(0);
	}

	// Initialize plcVertex and plcFaces
  	ply_set_read_cb(inputPLY, "vertex", "x", vertex_cb, NULL, 0);
	ply_set_read_cb(inputPLY, "vertex", "y", vertex_cb, NULL, 0);
	ply_set_read_cb(inputPLY, "vertex", "z", vertex_cb, NULL, 1);

	ply_set_read_cb(inputPLY, "face", "vertex_indices", face_cb, NULL, 0);

	ply_close(inputPLY);

}

// computes the initial delaunay tetrahedralization
void computeInitialDelaunayTetrahedralization()
{

	list <Point> tempPointList;
	unsigned int i = 0;

	for (map<unsigned, Point>::iterator pit = plcVertices.begin(); pit != plcVertices.end(); pit++, i++)
		tempPointList.push_front(plcVertices.find(i)->second);

    	DT.insert(tempPointList.begin(), tempPointList.end());

	cout << "\nInitial Delaunay tetrahedralization computed!!";
}

// removes local degeneracies from Delaunay tetrahedralization
void removeLocalDegeneracies()
{
	// compute all local degeneracies in DT and add them to Q
	// repeat while Q != NULL
		// for each local degeneracy l in Q:
			// if l is removable:
				// remove l by small perturbation
			// else,
				// compute vb to break l
					// if vb encroaches upon any segment or subface,
						// push l into queue
						// call boundary protection procedure
					// else
						// insert vb to break l
					// end if
			// end if
		// end for
	// end repeat
}

// recovers the constraint faces
void recoverConstraintFaces()
{

}

// main procedure
int main()
{
	readPLCInput();
	computeInitialDelaunayTetrahedralization();
	removeLocalDegeneracies();
	recoverConstraintFaces();
        return 0;
}
<commit_msg>added partial pseudocode for local degenracy removal<commit_after>#include <string.h>
#include <iostream>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Point_3.h>
#include <CGAL/Delaunay_triangulation_3.h>
#include "rply/rply.h"

using namespace std;
using namespace CGAL;

typedef Exact_predicates_inexact_constructions_kernel K;
typedef Point_3<K> Point;
typedef Delaunay_triangulation_3<K> Delaunay;

// PLC:
map <unsigned, Point> plcVertices; // mapping between vertex coordinates and corresponding unique id

class TriangleFace
{
	public:
		unsigned int vertexIds[3];
}tempFace;

class TetrahedronCell
{
	public:
		unsigned int vertexIds[4];
}tempTet;

list <TriangleFace> plcFaces; // contains ids of vertices/points making the triangle

// Delaunay tetrahedralization:
Delaunay DT;

// CDT:
map <unsigned, Point> cdtVertices;
list <TriangleFace> cdtTets; // contains ids of vertices making tetrahedron

static unsigned tempPoint[3];
unsigned int dimensionId = 0;
static unsigned pointId = 0;

static int vertex_cb(p_ply_argument argument)
{
	long eol;
	ply_get_argument_user_data(argument, NULL, &eol);
	tempPoint[dimensionId++] = ply_get_argument_value(argument);

	// insert the vertex into plcVertex
	if (eol)
	{
		plcVertices[pointId++] = Point(tempPoint[0],tempPoint[1],tempPoint[2]);
		dimensionId = 0;
	}
}

static int face_cb(p_ply_argument argument)
{
	long length, value_index;

	ply_get_argument_property(argument, NULL, &length, &value_index);

        switch (value_index)
	{
        	case 0:
	        case 1:
        		tempFace.vertexIds[pointId++] = ply_get_argument_value(argument);
	                break;
        	case 2:
			tempFace.vertexIds[pointId] = ply_get_argument_value(argument);
			pointId = 0;
			plcFaces.push_front(tempFace);
	                break;
        	default:
                	cout << "Invalid number of points in a face specified :(";
			break;
        }

}

// reads input PLC
void readPLCInput()
{
	// read PLY file(assumed to contain the PLC)
	string fileName;

	cout << "\nPlease enter input filename:\t";
	cin >> fileName;

	p_ply inputPLY = ply_open(fileName.c_str(), NULL, 0, NULL);

	if (!inputPLY) exit(0);

        if (!ply_read_header(inputPLY)) exit(0);

	if (!ply_read(inputPLY))
	{
		cout << "Cannot read the PLY file :(";
		exit(0);
	}

	// Initialize plcVertex and plcFaces
  	ply_set_read_cb(inputPLY, "vertex", "x", vertex_cb, NULL, 0);
	ply_set_read_cb(inputPLY, "vertex", "y", vertex_cb, NULL, 0);
	ply_set_read_cb(inputPLY, "vertex", "z", vertex_cb, NULL, 1);

	ply_set_read_cb(inputPLY, "face", "vertex_indices", face_cb, NULL, 0);

	ply_close(inputPLY);

}

// computes the initial delaunay tetrahedralization
void computeInitialDelaunayTetrahedralization()
{

	list <Point> tempPointList;
	unsigned int i = 0;

	for (map<unsigned, Point>::iterator pit = plcVertices.begin(); pit != plcVertices.end(); pit++, i++)
		tempPointList.push_front(plcVertices.find(i)->second);

    	DT.insert(tempPointList.begin(), tempPointList.end());

	cout << "\nInitial Delaunay tetrahedralization computed!!";
}

void addLocalDegeneracies(queue<VertexSet> degeneracyQueue)
{

	// For each tet 't' of DT
		// If it forms a local degeneracy with any of its neighbor vertex:(i.e., all vertices are co-spherical)
			// Add this 5 vertex set to degeneracyQueue

	// QUESTION: How to relate vertices in DT with those in the inputPLC??
		// Required for perturbing a vertex in the degenerate set.

	for (triangulation<>::iterator cellIter = DT.cellIter.begin(); cellIter != DT.cellIter.end(); cellIter++)
		{
			for (each neighbor vertex of tet, v)
				if (areCospherical(tetVertices, v))
				{
					VertexSet vs;
					vs.add(tetVertices);
					vs.add(v);
					degeneracyQueue.push_front(vs);
				}
		}

	removeDuplicateDegeneracies(degeneracyQueue);

}

// removes local degeneracies from Delaunay tetrahedralization
void removeLocalDegeneracies()
{

	cout << "\nStarting local degeneracy removal...";

	// compute all local degeneracies in DT and add them to Q
	queue<VertexSet> degeneracyQueue;
	addLocalDegeneracies(degeneracyQueue);

	// repeat while Q != NULL
	while (degenercyQueue.size() != 0)
		for (queue<>::iterator qIter = degeneracyQueue.begin(); qIter != degeneracyQueue.end(); qIter++)
			{
				if (isDegeneracyRemovable(qIter))
					perturbRemove(qIter, degeneracyQueue);
				else
				{
					Vertex vb;
					computeBreakPoint(vb, qIter, degeneracyQueue);
					if (isEncroachingPLC(vb))
						boundaryProtection(vb);
					else
						inputPLCVertices.push(vb);
				}
			}
		// for each local degeneracy l in Q:
			// if l is removable:
				// remove l by small perturbation
			// else,
				// compute vb to break l
					// if vb encroaches upon any segment or subface,
						// push l into queue
						// call boundary protection procedure
					// else
						// insert vb to break l
					// end if
			// end if
		// end for
	// end repeat
}

// recovers the constraint faces
void recoverConstraintFaces()
{

}

// main procedure
int main()
{
	readPLCInput();
	computeInitialDelaunayTetrahedralization();
	removeLocalDegeneracies();
	recoverConstraintFaces();
        return 0;
}
<|endoftext|>


<commit_before>#include <piecewiseLinearComplex.h>
#include <delaunayTriangulation.h>

// reads input PLC
void readPLCInput()
{
	// read PLY file(assumed to contain the PLC)
    	// initialize inputVertices
    	// initialize inputFaces

}

// computes the initial delaunay tetrahedralization
void computeInitialDelaunayTetrahedralization()
{

}

// removes local degeneracies from Delaunay tetrahedralization
void removeLocalDegeneracies()
{
	// compute all local degeneracies in DT and add them to Q
	// repeat while Q != NULL
		// for each local degeneracy l in Q:
			// if l is removable:
				// remove l by small perturbation
			// else,
				// compute vb to break l
					// if vb encroaches upon any segment or subface,
						// push l into queue
						// call boundary protection procedure
					// else
						// insert vb to break l
					// end if
			// end if
		// end for
	// end repeat
}

// recovers the constraint faces
void recoverConstraintFaces()
{

}

// main procedure
int main()
{
	readPLCInput();
	computeInitialDelaunayTetrahedralization();
	removeLocalDegeneracies();
	recoverConstraintFaces();
        return 0;
}
<commit_msg>corrected header files<commit_after>#include <CGAL/Linear_cell_complex.h>
#include <CGAL/Delaunay_triangulation_3.h>

// reads input PLC
void readPLCInput()
{
	// read PLY file(assumed to contain the PLC)
    	// initialize inputVertices
    	// initialize inputFaces

}

// computes the initial delaunay tetrahedralization
void computeInitialDelaunayTetrahedralization()
{

}

// removes local degeneracies from Delaunay tetrahedralization
void removeLocalDegeneracies()
{
	// compute all local degeneracies in DT and add them to Q
	// repeat while Q != NULL
		// for each local degeneracy l in Q:
			// if l is removable:
				// remove l by small perturbation
			// else,
				// compute vb to break l
					// if vb encroaches upon any segment or subface,
						// push l into queue
						// call boundary protection procedure
					// else
						// insert vb to break l
					// end if
			// end if
		// end for
	// end repeat
}

// recovers the constraint faces
void recoverConstraintFaces()
{

}

// main procedure
int main()
{
	readPLCInput();
	computeInitialDelaunayTetrahedralization();
	removeLocalDegeneracies();
	recoverConstraintFaces();
        return 0;
}
<|endoftext|>


<commit_before>/* http_auction_handler.cc
   Jeremy Barnes, 14 December 2012
   Copyright (c) 2012 Datacratic.  All rights reserved.

   Handler for auctions.
*/

#include "http_auction_handler.h"
#include "http_exchange_connector.h"

#include "jml/arch/exception.h"
#include "jml/arch/format.h"
#include "jml/arch/backtrace.h"
#include "jml/utils/guard.h"
#include "jml/utils/set_utils.h"
#include "jml/utils/vector_utils.h"
#include "jml/arch/timers.h"
#include <set>

#include <boost/foreach.hpp>

using namespace std;
using namespace ML;

namespace RTBKIT {

/*****************************************************************************/
/* HTTP AUCTION LOGGER                                                       */
/*****************************************************************************/

HttpAuctionLogger::
HttpAuctionLogger(std::string const & filename, int count) : requestFilename(filename), requestLimit(count) {
    requestCount = requestFile = 0;
}

void
HttpAuctionLogger::
recordRequest(HttpHeader const & headers, std::string const & body) {
    std::lock_guard<std::mutex> guard(lock);
    if(!stream) {
        std::string filename = ML::format("%d-%s", requestFile, requestFilename);
        stream.open(filename);
    }

    stream << headers << body << std::endl;
    ++requestCount;
    if(requestCount == requestLimit) {
        stream.close();
        ++requestFile;
        requestCount = 0;
    }
}

void
HttpAuctionLogger::
close() {
    if(stream) {
        stream.close();
    }
}

unsigned
HttpAuctionLogger::
parse(const std::string & filename,
      const std::function<void(const std::string &)> & callback)
{
    cerr << "reading packets from " << filename << endl;

    filter_istream stream(filename);
    ML::Parse_Context context(filename, stream);

    int count = 0;

    while(context) {
        try {
            Parse_Context::Hold_Token hold(context);

            while (context) {
                Parse_Context::Revert_Token token(context);
                if (context.match_literal("POST")) break;
                token.ignore();
                context.expect_line();
            }

            std::string request = context.expect_text("\r") + "\r\n";
            context.expect_literal("\r\n");

            int contentLength = -1;

            while (!context.eof()) {
                string line;
                if (context.match_literal("Content-Length:")) {
                    context.skip_whitespace();
                    contentLength = context.expect_int();
                    line = ML::format("Content-Length: %d", contentLength);
                }
                else {
                    line = context.expect_text("\r\n");
                }

                request += line + "\r\n";
                context.expect_literal("\r\n");

                if (line.empty()) break;
            }

            if (contentLength == -1)
                throw ML::Exception("no content-length");

            for (unsigned i = 0;  i < contentLength;  ++i) {
                context.match_literal('\n');
                request += *context++;
            }

            context.match_eol();

            callback(request);
            ++count;
        }
        catch (const std::exception & exc) {
            std::cerr << context.where() << ": got exception: "
                      << exc.what() << std::endl;
            break;
        }
    }

    return count;
}

/*****************************************************************************/
/* HTTP AUCTION HANDLER                                                      */
/*****************************************************************************/

long HttpAuctionHandler::created = 0;
long HttpAuctionHandler::destroyed = 0;

HttpAuctionHandler::
HttpAuctionHandler()
    : hasTimer(false), disconnected(false), servingRequest(false)
{
    atomic_add(created, 1);
}

HttpAuctionHandler::
~HttpAuctionHandler()
{
    if (servingRequest)
        ML::atomic_add(endpoint->numServingRequest_, -1);
    //cerr << "deleting HttpAuctionHandler at " << this << endl;
    //backtrace();
    atomic_add(destroyed, 1);
    if (hasTimer)
        throw Exception("deleted auction handler with timer");
}

void
HttpAuctionHandler::
onGotTransport()
{
    transport().activities.limit(5);
    //transport().activities.clear();

    addActivityS("gotTransport");

    //cerr << "bid handler got transport" << endl;

    this->endpoint = dynamic_cast<HttpExchangeConnector *>(get_endpoint());
    if (!this->endpoint)
        throw Exception("HttpAuctionHandler needs to be owned by an "
                        "HttpExchangeConnector");

    HttpConnectionHandler::onGotTransport();

    startReading();
}

void
HttpAuctionHandler::
handleDisconnect()
{
    doEvent("auctionDisconnection");

    disconnected = true;

    closeWhenHandlerFinished();
}

void
HttpAuctionHandler::
handleTimeout(Date date, size_t cookie)
{
    checkMagic();

    std::shared_ptr<Auction> auction_ = auction;
    if (auction->tooLate()) return;  // was sent before timeout

#if 0
    cerr << "auction " << auction_->id << " timed out with tooLate "
         << auction->tooLate() << " and response "
         << auction->getResponseJson().toString() << endl;
#endif

    if (!auction_) {
        cerr << "doAuctionTimeout with no auction" << endl;
        abort();
    }

    if (auction_->finish()) {
        doEvent("auctionTimeout");
        if (this->endpoint->onTimeout)
            this->endpoint->onTimeout(auction, date);
    }
}

void
HttpAuctionHandler::
cancelTimer()
{
    if (!hasTimer) return;

    //cerr << "cancelling timer" << endl;

    addActivityS("cancelTimer");
    ConnectionHandler::cancelTimer();
    hasTimer = false;
}

void
HttpAuctionHandler::
onDisassociate()
{
    //cerr << "onDisassociate" << endl;

    cancelTimer();

    /* We need to make sure that the handler doesn't try to send us
       anything. */
    if (auction && !auction->tooLate()) {
        auction->isZombie = true;
        doEvent("disconnectWithActiveAuction");
        //transport().activities.dump();
        //cerr << "disassociation of HttpAuctionHandler " << this
        //     << " when auction not finished"
        //     << endl;
        //backtrace();
        //throw Exception("attempt to disassociate HttpAuctionHandler when not "
        //                "too late: error %s", error.c_str());
    }

    endpoint->finishedWithHandler(shared_from_this());
}

void
HttpAuctionHandler::
onCleanup()
{
    onDisassociate();
}

void
HttpAuctionHandler::
doEvent(const char * eventName,
        EventType type,
        float value,
        const char * units)
{
    //cerr << eventName << " " << value << endl;
    endpoint->recordEvent(eventName, type, value);
}

void
HttpAuctionHandler::
incNumServingRequest()
{
    ML::atomic_add(endpoint->numServingRequest_, 1);
}

void
HttpAuctionHandler::
handleHttpPayload(const HttpHeader & header,
                  const std::string & payload)
{
    // Unknown resource?  Handle it...
    //cerr << header.verb << " " << header.resource << endl;
    //cerr << header << endl;
    //cerr << payload << endl;
    //cerr << endpoint->auctionVerb << " " << endpoint->auctionResource << endl;

    if (header.resource != endpoint->auctionResource
        || header.verb != endpoint->auctionVerb) {
        endpoint->handleUnknownRequest(*this, header, payload);
        return;
    }

    if(logger) {
        logger->recordRequest(header, payload);
    }

    doEvent("auctionReceived");

    incNumServingRequest();
    servingRequest = true;

    addActivityS("handleHttpPayload");

    stopReading();

    // First check if we are authorized to bid.  If not we drop the auction
    // with prejudice.
    Date now = Date::now();

    if (!endpoint->isEnabled(now)) {
        doEvent("auctionEarlyDrop.notEnabled");
        dropAuction("endpoint not enabled");
        return;
    }

    double acceptProbability = endpoint->acceptAuctionProbability;

    if (acceptProbability < 1.0
        && random() % 1000000 > 1000000 * acceptProbability) {
        // early drop...
        dropAuction("random early drop");
        return;
    }

    double timeAvailableMs = getTimeAvailableMs(header, payload);
    double networkTimeMs = getRoundTripTimeMs(header);

    doEvent("auctionStartLatencyMs",
            ET_OUTCOME,
            now.secondsSince(firstData) * 1000.0, "ms");

    doEvent("auctionTimeAvailableMs",
            ET_OUTCOME,
            timeAvailableMs, "ms");

    if (timeAvailableMs - networkTimeMs < 5.0) {
        // Do an early drop of the bid request without even creating an
        // auction

        doEvent("auctionEarlyDrop.timeLeftMs",
                ET_OUTCOME,
                timeAvailableMs, "ms");

        doEvent(ML::format("auctionEarlyDrop.peer.%s",
                               transport().getPeerName().c_str()).c_str());

        dropAuction(ML::format("timeleft of %f is too low",
                               timeAvailableMs));

        return;
    }

    /* This is the callback that the auction will call once finish()
       has been called on it.  It will be called exactly once.
    */
    auto handleAuction = [=] (std::shared_ptr<Auction> auction)
        {
            //cerr << "HANDLE AUCTION CALLED AFTER "
            //<< Date::now().secondsSince(auction->start) * 1000
            //<< "ms" << endl;

            if (!auction || auction->isZombie)
                return;  // Was already externally terminated; this is invalid

            if (this->transport().lockedByThisThread())
                this->sendResponse();
            else {
                this->transport()
                    .doAsync(boost::bind(&HttpAuctionHandler::sendResponse,
                                         this),
                             "AsyncSendResponse");
            }
        };

    // We always give ourselves 5ms to bid in, no matter what (let upstream
    // deal with it if it's really that much too slow).
    Date expiry = firstData.plusSeconds
        (max(5.0, (timeAvailableMs - networkTimeMs)) / 1000.0);

    try {
        auto bidRequest = parseBidRequest(header, payload);

        if (!bidRequest) {
            cerr << "got no bid request" << endl;
            // The request was handled; nothing to do
            return;
        }

        auction.reset(new Auction(endpoint,
                                  handleAuction, bidRequest,
                                  bidRequest->toJsonStr(),
                                  "datacratic",
                                  firstData, expiry));

#if 0
        static std::mutex lock;
        std::unique_lock<std::mutex> guard(lock);
        cerr << "bytes before = " << payload.size() << " after "
             << auction->requestStr.size() << " ratio "
             << 100.0 * auction->requestStr.size() / payload.size()
             << "%" << endl;
        string s = bidRequest->serializeToString();
        cerr << "serialized bytes before = " << payload.size() << " after "
             << s.size() << " ratio "
             << 100.0 * s.size() / payload.size() << "%" << endl;
#endif

    } catch (const std::exception & exc) {
        // Inject the error message in
        sendErrorResponse("Error parsing bid request", exc.what());
        return;
    }

    doEvent("auctionNetworkLatencyMs",
            ET_OUTCOME,
            (firstData.secondsSince(auction->request->timestamp)) * 1000.0,
            "ms");

    doEvent("auctionTotalStartLatencyMs",
            ET_OUTCOME,
            (now.secondsSince(auction->request->timestamp)) * 1000.0,
            "ms");

    doEvent("auctionStart");

    addActivity("gotAuction %s", auction->id.toString().c_str());

    if (now > expiry) {
        doEvent("auctionAlreadyExpired");

        string msg = format("auction started after time already elapsed: "
                            "%s vs %s, available time = %.1fms, "
                            "firstData = %s",
                            now.print(4).c_str(),
                            expiry.print(4).c_str(),
                            timeAvailableMs,
                            firstData.print(4).c_str());
        cerr << msg << endl;

        dropAuction("auction already expired");

        return;
    }

    addActivity("timeAvailable: %.1fms", timeAvailableMs);

    scheduleTimerAbsolute(expiry, 1);
    hasTimer = true;

    addActivity("gotTimer for %s",
                expiry.print(4).c_str());

    auction->doneParsing = Date::now();
    endpoint->onNewAuction(auction);
}

void
HttpAuctionHandler::
sendResponse()
{
    checkMagic();

    if (!transport().lockedByThisThread())
        throw Exception("sendResponse must be in handler context");

    addActivityS("sendResponse");

    //cerr << "locked by " << bid->lock.get_thread_id() << endl;
    //cerr << "my thread " << ACE_OS::thr_self() << endl;

    Date before = Date::now();

    /* Make sure the transport isn't dead. */
    transport().checkMagic();

    if (!transport().lockedByThisThread())
        throw Exception("transport not locked by this thread");

    if (!auction) {
        throw Exception("sending response for cleared auction");
    }

    if (!auction->tooLate())
        throw Exception("auction is not finished");

    addActivity("sendResponse (lock took %.2fms)",
                Date::now().secondsSince(before) * 1000);

    cancelTimer();

    endpoint->onAuctionDone(auction);

    //cerr << "sendResponse " << this << ": disconnected "
    //     << disconnected << endl;

    if (disconnected) {
        closeWhenHandlerFinished();
        return;
    }

    HttpResponse response = getResponse();

    Date startTime = auction->start;
    Date beforeSend = Date::now();

    auto onSendFinished = [=] ()
        {
            //static int n = 0;
            //ML::atomic_add(n, 1);
            //cerr << "sendFinished canBlock = " << canBlock << " "
            //<< n << endl;
            this->addActivityS("sendFinished");
            double sendTime = Date::now().secondsSince(beforeSend);
            if (sendTime > 0.01)
                cerr << "sendTime = " << sendTime << " for "
                     << (auction ? auction->id.toString() : "NO AUCTION")
                     << endl;

            this->doEvent("auctionResponseSent");
            this->doEvent("auctionTotalTimeMs",
                          ET_OUTCOME,
                          Date::now().secondsSince(this->firstData) * 1000.0,
                          "ms");

            if (random() % 1000 == 0) {
                this->transport().closeWhenHandlerFinished();
            }
            else {
                this->transport().associateWhenHandlerFinished
                (this->makeNewHandlerShared(), "sendFinished");
            }
        };

    addActivityS("beforeSend");

    double timeTaken = beforeSend.secondsSince(startTime) * 1000;

    response.extraHeaders
        .push_back({"X-Processing-Time-Ms", to_string(timeTaken)});

    putResponseOnWire(response, onSendFinished);
}

void
HttpAuctionHandler::
dropAuction(const std::string & reason)
{
    auto onSendFinished = [=] ()
        {
            if (random() % 1000 == 0) {
                this->transport().closeWhenHandlerFinished();
            }
            else {
                this->transport().associateWhenHandlerFinished
                (this->makeNewHandlerShared(), "sendFinished");
            }
        };

    putResponseOnWire(endpoint->getDroppedAuctionResponse(*this, reason),
                      onSendFinished);
}

void
HttpAuctionHandler::
sendErrorResponse(const std::string & error,
                  const std::string & details)
{
    putResponseOnWire(endpoint->getErrorResponse(*this, *auction, error + ": " + details));
}

std::string
HttpAuctionHandler::
status() const
{
    if (!hasTransport())
        return "newly minted HttpAuctionHandler";

    string result = format("GenericHttpAuctionHandler: %p readState %d hasTimer %d",
                           this, readState, hasTimer);
    result += "auction: ";
    if (auction) result += getResponse().body;
    else result += "NULL";
    return result;
}

HttpResponse
HttpAuctionHandler::
getResponse() const
{
    return endpoint->getResponse(*this, this->header, *auction);
}

std::shared_ptr<BidRequest>
HttpAuctionHandler::
parseBidRequest(const HttpHeader & header,
                const std::string & payload)
{
    return endpoint->parseBidRequest(*this, header, payload);
}

double
HttpAuctionHandler::
getTimeAvailableMs(const HttpHeader & header,
                   const std::string & payload)
{
    return endpoint->getTimeAvailableMs(*this, header, payload);
}

double
HttpAuctionHandler::
getRoundTripTimeMs(const HttpHeader & header)
{
    return endpoint->getRoundTripTimeMs(*this, header);
}

} // namespace RTBKIT
<commit_msg>log the auction body size to Carbon<commit_after>/* http_auction_handler.cc
   Jeremy Barnes, 14 December 2012
   Copyright (c) 2012 Datacratic.  All rights reserved.

   Handler for auctions.
*/

#include "http_auction_handler.h"
#include "http_exchange_connector.h"

#include "jml/arch/exception.h"
#include "jml/arch/format.h"
#include "jml/arch/backtrace.h"
#include "jml/utils/guard.h"
#include "jml/utils/set_utils.h"
#include "jml/utils/vector_utils.h"
#include "jml/arch/timers.h"
#include <set>

#include <boost/foreach.hpp>

using namespace std;
using namespace ML;

namespace RTBKIT {

/*****************************************************************************/
/* HTTP AUCTION LOGGER                                                       */
/*****************************************************************************/

HttpAuctionLogger::
HttpAuctionLogger(std::string const & filename, int count) : requestFilename(filename), requestLimit(count) {
    requestCount = requestFile = 0;
}

void
HttpAuctionLogger::
recordRequest(HttpHeader const & headers, std::string const & body) {
    std::lock_guard<std::mutex> guard(lock);
    if(!stream) {
        std::string filename = ML::format("%d-%s", requestFile, requestFilename);
        stream.open(filename);
    }

    stream << headers << body << std::endl;
    ++requestCount;
    if(requestCount == requestLimit) {
        stream.close();
        ++requestFile;
        requestCount = 0;
    }
}

void
HttpAuctionLogger::
close() {
    if(stream) {
        stream.close();
    }
}

unsigned
HttpAuctionLogger::
parse(const std::string & filename,
      const std::function<void(const std::string &)> & callback)
{
    cerr << "reading packets from " << filename << endl;

    filter_istream stream(filename);
    ML::Parse_Context context(filename, stream);

    int count = 0;

    while(context) {
        try {
            Parse_Context::Hold_Token hold(context);

            while (context) {
                Parse_Context::Revert_Token token(context);
                if (context.match_literal("POST")) break;
                token.ignore();
                context.expect_line();
            }

            std::string request = context.expect_text("\r") + "\r\n";
            context.expect_literal("\r\n");

            int contentLength = -1;

            while (!context.eof()) {
                string line;
                if (context.match_literal("Content-Length:")) {
                    context.skip_whitespace();
                    contentLength = context.expect_int();
                    line = ML::format("Content-Length: %d", contentLength);
                }
                else {
                    line = context.expect_text("\r\n");
                }

                request += line + "\r\n";
                context.expect_literal("\r\n");

                if (line.empty()) break;
            }

            if (contentLength == -1)
                throw ML::Exception("no content-length");

            for (unsigned i = 0;  i < contentLength;  ++i) {
                context.match_literal('\n');
                request += *context++;
            }

            context.match_eol();

            callback(request);
            ++count;
        }
        catch (const std::exception & exc) {
            std::cerr << context.where() << ": got exception: "
                      << exc.what() << std::endl;
            break;
        }
    }

    return count;
}

/*****************************************************************************/
/* HTTP AUCTION HANDLER                                                      */
/*****************************************************************************/

long HttpAuctionHandler::created = 0;
long HttpAuctionHandler::destroyed = 0;

HttpAuctionHandler::
HttpAuctionHandler()
    : hasTimer(false), disconnected(false), servingRequest(false)
{
    atomic_add(created, 1);
}

HttpAuctionHandler::
~HttpAuctionHandler()
{
    if (servingRequest)
        ML::atomic_add(endpoint->numServingRequest_, -1);
    //cerr << "deleting HttpAuctionHandler at " << this << endl;
    //backtrace();
    atomic_add(destroyed, 1);
    if (hasTimer)
        throw Exception("deleted auction handler with timer");
}

void
HttpAuctionHandler::
onGotTransport()
{
    transport().activities.limit(5);
    //transport().activities.clear();

    addActivityS("gotTransport");

    //cerr << "bid handler got transport" << endl;

    this->endpoint = dynamic_cast<HttpExchangeConnector *>(get_endpoint());
    if (!this->endpoint)
        throw Exception("HttpAuctionHandler needs to be owned by an "
                        "HttpExchangeConnector");

    HttpConnectionHandler::onGotTransport();

    startReading();
}

void
HttpAuctionHandler::
handleDisconnect()
{
    doEvent("auctionDisconnection");

    disconnected = true;

    closeWhenHandlerFinished();
}

void
HttpAuctionHandler::
handleTimeout(Date date, size_t cookie)
{
    checkMagic();

    std::shared_ptr<Auction> auction_ = auction;
    if (auction->tooLate()) return;  // was sent before timeout

#if 0
    cerr << "auction " << auction_->id << " timed out with tooLate "
         << auction->tooLate() << " and response "
         << auction->getResponseJson().toString() << endl;
#endif

    if (!auction_) {
        cerr << "doAuctionTimeout with no auction" << endl;
        abort();
    }

    if (auction_->finish()) {
        doEvent("auctionTimeout");
        if (this->endpoint->onTimeout)
            this->endpoint->onTimeout(auction, date);
    }
}

void
HttpAuctionHandler::
cancelTimer()
{
    if (!hasTimer) return;

    //cerr << "cancelling timer" << endl;

    addActivityS("cancelTimer");
    ConnectionHandler::cancelTimer();
    hasTimer = false;
}

void
HttpAuctionHandler::
onDisassociate()
{
    //cerr << "onDisassociate" << endl;

    cancelTimer();

    /* We need to make sure that the handler doesn't try to send us
       anything. */
    if (auction && !auction->tooLate()) {
        auction->isZombie = true;
        doEvent("disconnectWithActiveAuction");
        //transport().activities.dump();
        //cerr << "disassociation of HttpAuctionHandler " << this
        //     << " when auction not finished"
        //     << endl;
        //backtrace();
        //throw Exception("attempt to disassociate HttpAuctionHandler when not "
        //                "too late: error %s", error.c_str());
    }

    endpoint->finishedWithHandler(shared_from_this());
}

void
HttpAuctionHandler::
onCleanup()
{
    onDisassociate();
}

void
HttpAuctionHandler::
doEvent(const char * eventName,
        EventType type,
        float value,
        const char * units)
{
    //cerr << eventName << " " << value << endl;
    endpoint->recordEvent(eventName, type, value);
}

void
HttpAuctionHandler::
incNumServingRequest()
{
    ML::atomic_add(endpoint->numServingRequest_, 1);
}

void
HttpAuctionHandler::
handleHttpPayload(const HttpHeader & header,
                  const std::string & payload)
{
    // Unknown resource?  Handle it...
    //cerr << header.verb << " " << header.resource << endl;
    //cerr << header << endl;
    //cerr << payload << endl;
    //cerr << endpoint->auctionVerb << " " << endpoint->auctionResource << endl;

    if (header.resource != endpoint->auctionResource
        || header.verb != endpoint->auctionVerb) {
        endpoint->handleUnknownRequest(*this, header, payload);
        return;
    }

    if(logger) {
        logger->recordRequest(header, payload);
    }

    doEvent("auctionReceived");
    doEvent("auctionBodySize", ET_COUNT, payload.size());

    incNumServingRequest();
    servingRequest = true;

    addActivityS("handleHttpPayload");

    stopReading();

    // First check if we are authorized to bid.  If not we drop the auction
    // with prejudice.
    Date now = Date::now();

    if (!endpoint->isEnabled(now)) {
        doEvent("auctionEarlyDrop.notEnabled");
        dropAuction("endpoint not enabled");
        return;
    }

    double acceptProbability = endpoint->acceptAuctionProbability;

    if (acceptProbability < 1.0
        && random() % 1000000 > 1000000 * acceptProbability) {
        // early drop...
        dropAuction("random early drop");
        return;
    }

    double timeAvailableMs = getTimeAvailableMs(header, payload);
    double networkTimeMs = getRoundTripTimeMs(header);

    doEvent("auctionStartLatencyMs",
            ET_OUTCOME,
            now.secondsSince(firstData) * 1000.0, "ms");

    doEvent("auctionTimeAvailableMs",
            ET_OUTCOME,
            timeAvailableMs, "ms");

    if (timeAvailableMs - networkTimeMs < 5.0) {
        // Do an early drop of the bid request without even creating an
        // auction

        doEvent("auctionEarlyDrop.timeLeftMs",
                ET_OUTCOME,
                timeAvailableMs, "ms");

        doEvent(ML::format("auctionEarlyDrop.peer.%s",
                               transport().getPeerName().c_str()).c_str());

        dropAuction(ML::format("timeleft of %f is too low",
                               timeAvailableMs));

        return;
    }

    /* This is the callback that the auction will call once finish()
       has been called on it.  It will be called exactly once.
    */
    auto handleAuction = [=] (std::shared_ptr<Auction> auction)
        {
            //cerr << "HANDLE AUCTION CALLED AFTER "
            //<< Date::now().secondsSince(auction->start) * 1000
            //<< "ms" << endl;

            if (!auction || auction->isZombie)
                return;  // Was already externally terminated; this is invalid

            if (this->transport().lockedByThisThread())
                this->sendResponse();
            else {
                this->transport()
                    .doAsync(boost::bind(&HttpAuctionHandler::sendResponse,
                                         this),
                             "AsyncSendResponse");
            }
        };

    // We always give ourselves 5ms to bid in, no matter what (let upstream
    // deal with it if it's really that much too slow).
    Date expiry = firstData.plusSeconds
        (max(5.0, (timeAvailableMs - networkTimeMs)) / 1000.0);

    try {
        auto bidRequest = parseBidRequest(header, payload);

        if (!bidRequest) {
            cerr << "got no bid request" << endl;
            // The request was handled; nothing to do
            return;
        }

        auction.reset(new Auction(endpoint,
                                  handleAuction, bidRequest,
                                  bidRequest->toJsonStr(),
                                  "datacratic",
                                  firstData, expiry));

#if 0
        static std::mutex lock;
        std::unique_lock<std::mutex> guard(lock);
        cerr << "bytes before = " << payload.size() << " after "
             << auction->requestStr.size() << " ratio "
             << 100.0 * auction->requestStr.size() / payload.size()
             << "%" << endl;
        string s = bidRequest->serializeToString();
        cerr << "serialized bytes before = " << payload.size() << " after "
             << s.size() << " ratio "
             << 100.0 * s.size() / payload.size() << "%" << endl;
#endif

    } catch (const std::exception & exc) {
        // Inject the error message in
        sendErrorResponse("Error parsing bid request", exc.what());
        return;
    }

    doEvent("auctionNetworkLatencyMs",
            ET_OUTCOME,
            (firstData.secondsSince(auction->request->timestamp)) * 1000.0,
            "ms");

    doEvent("auctionTotalStartLatencyMs",
            ET_OUTCOME,
            (now.secondsSince(auction->request->timestamp)) * 1000.0,
            "ms");

    doEvent("auctionStart");

    addActivity("gotAuction %s", auction->id.toString().c_str());

    if (now > expiry) {
        doEvent("auctionAlreadyExpired");

        string msg = format("auction started after time already elapsed: "
                            "%s vs %s, available time = %.1fms, "
                            "firstData = %s",
                            now.print(4).c_str(),
                            expiry.print(4).c_str(),
                            timeAvailableMs,
                            firstData.print(4).c_str());
        cerr << msg << endl;

        dropAuction("auction already expired");

        return;
    }

    addActivity("timeAvailable: %.1fms", timeAvailableMs);

    scheduleTimerAbsolute(expiry, 1);
    hasTimer = true;

    addActivity("gotTimer for %s",
                expiry.print(4).c_str());

    auction->doneParsing = Date::now();
    endpoint->onNewAuction(auction);
}

void
HttpAuctionHandler::
sendResponse()
{
    checkMagic();

    if (!transport().lockedByThisThread())
        throw Exception("sendResponse must be in handler context");

    addActivityS("sendResponse");

    //cerr << "locked by " << bid->lock.get_thread_id() << endl;
    //cerr << "my thread " << ACE_OS::thr_self() << endl;

    Date before = Date::now();

    /* Make sure the transport isn't dead. */
    transport().checkMagic();

    if (!transport().lockedByThisThread())
        throw Exception("transport not locked by this thread");

    if (!auction) {
        throw Exception("sending response for cleared auction");
    }

    if (!auction->tooLate())
        throw Exception("auction is not finished");

    addActivity("sendResponse (lock took %.2fms)",
                Date::now().secondsSince(before) * 1000);

    cancelTimer();

    endpoint->onAuctionDone(auction);

    //cerr << "sendResponse " << this << ": disconnected "
    //     << disconnected << endl;

    if (disconnected) {
        closeWhenHandlerFinished();
        return;
    }

    HttpResponse response = getResponse();

    Date startTime = auction->start;
    Date beforeSend = Date::now();

    auto onSendFinished = [=] ()
        {
            //static int n = 0;
            //ML::atomic_add(n, 1);
            //cerr << "sendFinished canBlock = " << canBlock << " "
            //<< n << endl;
            this->addActivityS("sendFinished");
            double sendTime = Date::now().secondsSince(beforeSend);
            if (sendTime > 0.01)
                cerr << "sendTime = " << sendTime << " for "
                     << (auction ? auction->id.toString() : "NO AUCTION")
                     << endl;

            this->doEvent("auctionResponseSent");
            this->doEvent("auctionTotalTimeMs",
                          ET_OUTCOME,
                          Date::now().secondsSince(this->firstData) * 1000.0,
                          "ms");

            if (random() % 1000 == 0) {
                this->transport().closeWhenHandlerFinished();
            }
            else {
                this->transport().associateWhenHandlerFinished
                (this->makeNewHandlerShared(), "sendFinished");
            }
        };

    addActivityS("beforeSend");

    double timeTaken = beforeSend.secondsSince(startTime) * 1000;

    response.extraHeaders
        .push_back({"X-Processing-Time-Ms", to_string(timeTaken)});

    putResponseOnWire(response, onSendFinished);
}

void
HttpAuctionHandler::
dropAuction(const std::string & reason)
{
    auto onSendFinished = [=] ()
        {
            if (random() % 1000 == 0) {
                this->transport().closeWhenHandlerFinished();
            }
            else {
                this->transport().associateWhenHandlerFinished
                (this->makeNewHandlerShared(), "sendFinished");
            }
        };

    putResponseOnWire(endpoint->getDroppedAuctionResponse(*this, reason),
                      onSendFinished);
}

void
HttpAuctionHandler::
sendErrorResponse(const std::string & error,
                  const std::string & details)
{
    putResponseOnWire(endpoint->getErrorResponse(*this, *auction, error + ": " + details));
}

std::string
HttpAuctionHandler::
status() const
{
    if (!hasTransport())
        return "newly minted HttpAuctionHandler";

    string result = format("GenericHttpAuctionHandler: %p readState %d hasTimer %d",
                           this, readState, hasTimer);
    result += "auction: ";
    if (auction) result += getResponse().body;
    else result += "NULL";
    return result;
}

HttpResponse
HttpAuctionHandler::
getResponse() const
{
    return endpoint->getResponse(*this, this->header, *auction);
}

std::shared_ptr<BidRequest>
HttpAuctionHandler::
parseBidRequest(const HttpHeader & header,
                const std::string & payload)
{
    return endpoint->parseBidRequest(*this, header, payload);
}

double
HttpAuctionHandler::
getTimeAvailableMs(const HttpHeader & header,
                   const std::string & payload)
{
    return endpoint->getTimeAvailableMs(*this, header, payload);
}

double
HttpAuctionHandler::
getRoundTripTimeMs(const HttpHeader & header)
{
    return endpoint->getRoundTripTimeMs(*this, header);
}

} // namespace RTBKIT
<|endoftext|>