<commit_before>/**
 *  @file CVodesIntegrator.cpp
 *
 */

// Copyright 2001  California Institute of Technology

#include "CVodesIntegrator.h"
#include "stringUtils.h"

#include <iostream>
using namespace std;

#ifdef SUNDIALS_VERSION_22

#include <sundials_types.h>
#include <sundials_math.h>
#include <cvodes.h>
#include <cvodes_dense.h>
#include <cvodes_diag.h>
#include <cvodes_spgmr.h>
#include <cvodes_band.h>
#include <nvector_serial.h>

#else

#ifdef SUNDIALS_VERSION_23
#include <sundials/sundials_types.h>
#include <sundials/sundials_math.h>
#include <sundials/sundials_nvector.h>
#include <nvector/nvector_serial.h>
#include <cvodes/cvodes.h>
#include <cvodes/cvodes_dense.h>
#include <cvodes/cvodes_diag.h>
#include <cvodes/cvodes_spgmr.h>
#include <cvodes/cvodes_band.h>

#else

unsupported sundials version!

#endif

#endif

inline static N_Vector nv(void* x) {
    return reinterpret_cast<N_Vector>(x);
}

namespace Cantera {

    class FuncData {
    public:
        FuncData(FuncEval* f, int npar = 0) {
            m_pars.resize(npar, 1.0);
            m_func = f;
        }
        virtual ~FuncData() {}
        vector_fp m_pars;
        FuncEval* m_func;
    };
}

extern "C" {

    /**
     *  Function called by cvodes to evaluate ydot given y.  The cvode
     *  integrator allows passing in a void* pointer to access
     *  external data. This pointer is cast to a pointer to a instance
     *  of class FuncEval. The equations to be integrated should be
     *  specified by deriving a class from FuncEval that evaluates the
     *  desired equations.
     *  @ingroup odeGroup
     */
    static int cvodes_rhs(realtype t, N_Vector y, N_Vector ydot,
        void *f_data) {
        double* ydata = NV_DATA_S(y); //N_VDATA(y);
        double* ydotdata = NV_DATA_S(ydot); //N_VDATA(ydot);
        Cantera::FuncData* d = (Cantera::FuncData*)f_data;
        Cantera::FuncEval* f = d->m_func;
        //try {
            if (d->m_pars.size() == 0)
                f->eval(t, ydata, ydotdata, NULL);
            else
                f->eval(t, ydata, ydotdata, DATA_PTR(d->m_pars));
            //}
            //catch (...) {
            //Cantera::showErrors();
            //Cantera::error("Teminating execution");
            //}
            return 0;
    }

}

namespace Cantera {

    /**
     *  Constructor. Default settings: dense jacobian, no user-supplied
     *  Jacobian function, Newton iteration.
     */
    CVodesIntegrator::CVodesIntegrator() : m_neq(0),
                           m_cvode_mem(0),
                           m_t0(0.0),
                           m_y(0),
                           m_abstol(0),
                           m_type(DENSE+NOJAC),
                           m_itol(CV_SS),
                           m_method(CV_BDF),
                           m_iter(CV_NEWTON),
                           m_maxord(0),
                           m_reltol(1.e-9),
                           m_abstols(1.e-15),
                                           m_reltolsens(1.0e-5),
                                           m_abstolsens(1.0e-4),
                           m_nabs(0),
                           m_hmax(0.0),
                                           m_maxsteps(20000), m_np(0),
                                           m_mupper(0), m_mlower(0)
    {
        //m_ropt.resize(OPT_SIZE,0.0);
        //m_iopt = new long[OPT_SIZE];
        //fill(m_iopt, m_iopt+OPT_SIZE,0);
    }

    /// Destructor.
    CVodesIntegrator::~CVodesIntegrator()
    {
        if (m_cvode_mem) {
            if (m_np > 0)
                CVodeSensFree(m_cvode_mem);
            CVodeFree(&m_cvode_mem);
        }
      if (m_y) N_VDestroy_Serial(nv(m_y));
      if (m_abstol) N_VDestroy_Serial(nv(m_abstol));
      delete m_fdata;

      //delete[] m_iopt;
    }

    double& CVodesIntegrator::solution(int k){
        return NV_Ith_S(nv(m_y),k);
    }

    double* CVodesIntegrator::solution(){ return NV_DATA_S(nv(m_y));
    }

    void CVodesIntegrator::setTolerances(double reltol, int n, double* abstol) {
        m_itol = CV_SV;
        m_nabs = n;
        if (n != m_neq) {
            if (m_abstol) N_VDestroy_Serial(nv(m_abstol));
            m_abstol = reinterpret_cast<void*>(N_VNew_Serial(n));
        }
        for (int i=0; i<n; i++) {
            NV_Ith_S(nv(m_abstol), i) = abstol[i];
        }
        m_reltol = reltol;
    }

    void CVodesIntegrator::setTolerances(double reltol, double abstol) {
        m_itol = CV_SS;
        m_reltol = reltol;
        m_abstols = abstol;
    }

    void CVodesIntegrator::setSensitivityTolerances(double reltol, double abstol) {
        m_reltolsens = reltol;
        m_abstolsens = abstol;
    }

    void CVodesIntegrator::setProblemType(int probtype) {
      m_type = probtype;
    }

    void CVodesIntegrator::setMethod(MethodType t) {
        if (t == BDF_Method)
            m_method = CV_BDF;
        else if (t == Adams_Method)
            m_method = CV_ADAMS;
        else
            throw CVodesErr("unknown method");
    }

    void CVodesIntegrator::setMaxStepSize(doublereal hmax) {
        m_hmax = hmax;
        if (m_cvode_mem)
            CVodeSetMaxStep(m_cvode_mem, hmax);
    }

    void CVodesIntegrator::setMinStepSize(doublereal hmin) {
        m_hmin = hmin;
        if (m_cvode_mem)
            CVodeSetMinStep(m_cvode_mem, hmin);
    }

    void CVodesIntegrator::setMaxSteps(int nmax) {
        m_maxsteps = nmax;
        if (m_cvode_mem)
            CVodeSetMaxNumSteps(m_cvode_mem, m_maxsteps);
    }

    void CVodesIntegrator::setIterator(IterType t) {
        if (t == Newton_Iter)
            m_iter = CV_NEWTON;
        else if (t == Functional_Iter)
            m_iter = CV_FUNCTIONAL;
        else
            throw CVodesErr("unknown iterator");
    }

    void CVodesIntegrator::sensInit(double t0, FuncEval& func) {
        m_np = func.nparams();
        long int nv = func.neq();

        doublereal* data;
        int n, j;
        N_Vector y;
        y = N_VNew_Serial(nv);
        m_yS = N_VCloneVectorArray_Serial(m_np, y);
        for (n = 0; n < m_np; n++) {
            data = NV_DATA_S(m_yS[n]);
            for (j = 0; j < nv; j++) {
                data[j] =0.0;
            }
        }

        int flag;
        flag = CVodeSensMalloc(m_cvode_mem, m_np, CV_STAGGERED, m_yS);
        if (flag != CV_SUCCESS)
            throw CVodesErr("Error in CVodeSensMalloc");
        vector_fp atol(m_np, m_abstolsens);
        double rtol = m_reltolsens;
        flag = CVodeSetSensTolerances(m_cvode_mem, CV_SS, rtol, DATA_PTR(atol));
    }

    void CVodesIntegrator::initialize(double t0, FuncEval& func)
    {
        m_neq = func.neq();
        m_t0  = t0;

        if (m_y) {
            N_VDestroy_Serial(nv(m_y));    // free solution vector if already allocated
        }
        m_y = reinterpret_cast<void*>(N_VNew_Serial(m_neq));   // allocate solution vector
        for (int i=0; i<m_neq; i++) {
            NV_Ith_S(nv(m_y), i) = 0.0;
        }
        // check abs tolerance array size
        if (m_itol == CV_SV && m_nabs < m_neq)
            throw CVodesErr("not enough absolute tolerance values specified.");

        func.getInitialConditions(m_t0, m_neq, NV_DATA_S(nv(m_y)));

        if (m_cvode_mem) CVodeFree(&m_cvode_mem);
        m_cvode_mem = CVodeCreate(m_method, m_iter);
        if (!m_cvode_mem) throw CVodesErr("CVodeCreate failed.");

        int flag = 0;
        if (m_itol == CV_SV) {
            // vector atol
            flag = CVodeMalloc(m_cvode_mem, cvodes_rhs, m_t0, nv(m_y), m_itol,
                m_reltol, nv(m_abstol));
        }
        else {
            // scalar atol
            flag = CVodeMalloc(m_cvode_mem, cvodes_rhs, m_t0, nv(m_y), m_itol,
                m_reltol, &m_abstols);
        }
        if (flag != CV_SUCCESS) {
            if (flag == CV_MEM_FAIL) {
                throw CVodesErr("Memory allocation failed.");
            }
            else if (flag == CV_ILL_INPUT) {
                throw CVodesErr("Illegal value for CVodeMalloc input argument.");
            }
            else
                throw CVodesErr("CVodeMalloc failed.");
        }

        if (m_type == DENSE + NOJAC) {
            long int N = m_neq;
            CVDense(m_cvode_mem, N);
        }
        else if (m_type == DIAG) {
            CVDiag(m_cvode_mem);
        }
        else if (m_type == GMRES) {
            CVSpgmr(m_cvode_mem, PREC_NONE, 0);
        }
        else if (m_type == BAND + NOJAC) {
            long int N = m_neq;
            long int nu = m_mupper;
            long int nl = m_mlower;
            CVBand(m_cvode_mem, N, nu, nl);
        }
        else {
            throw CVodesErr("unsupported option");
        }

        // pass a pointer to func in m_data
        m_fdata = new FuncData(&func, func.nparams());

        //m_data = (void*)&func;

        flag = CVodeSetFdata(m_cvode_mem, (void*)m_fdata);
        if (flag != CV_SUCCESS)
            throw CVodesErr("CVodeSetFdata failed.");

        if (func.nparams() > 0) {
            sensInit(t0, func);
            flag = CVodeSetSensParams(m_cvode_mem, DATA_PTR(m_fdata->m_pars),
                NULL, NULL);
        }

        // set options
        if (m_maxord > 0)
            flag = CVodeSetMaxOrd(m_cvode_mem, m_maxord);
        if (m_maxsteps > 0)
            flag = CVodeSetMaxNumSteps(m_cvode_mem, m_maxsteps);
        if (m_hmax > 0)
            flag = CVodeSetMaxStep(m_cvode_mem, m_hmax);
    }

    void CVodesIntegrator::reinitialize(double t0, FuncEval& func)
    {
        m_t0  = t0;
        //try {
            func.getInitialConditions(m_t0, m_neq, NV_DATA_S(nv(m_y)));
            //}
            //catch (CanteraError) {
            //showErrors();
            //error("Teminating execution");
            //}

        int result, flag;
        if (m_itol == CV_SV) {
            result = CVodeReInit(m_cvode_mem, cvodes_rhs, m_t0, nv(m_y),
                m_itol, m_reltol,
                nv(m_abstol));
        }
        else {
            result = CVodeReInit(m_cvode_mem, cvodes_rhs, m_t0, nv(m_y),
                m_itol, m_reltol,
                &m_abstols);
        }
        if (result != CV_SUCCESS)
            throw CVodesErr("CVReInit failed. result = "+int2str(result));

        if (m_type == DENSE + NOJAC) {
            long int N = m_neq;
            CVDense(m_cvode_mem, N);
        }
        else if (m_type == DIAG) {
            CVDiag(m_cvode_mem);
        }
        else if (m_type == BAND + NOJAC) {
            long int N = m_neq;
            long int nu = m_mupper;
            long int nl = m_mlower;
            CVBand(m_cvode_mem, N, nu, nl);
        }
        else if (m_type == GMRES) {
            CVSpgmr(m_cvode_mem, PREC_NONE, 0);
        }
        else {
            throw CVodesErr("unsupported option");
        }

        // set options
        if (m_maxord > 0)
            flag = CVodeSetMaxOrd(m_cvode_mem, m_maxord);
        if (m_maxsteps > 0)
            flag = CVodeSetMaxNumSteps(m_cvode_mem, m_maxsteps);
        if (m_hmax > 0)
            flag = CVodeSetMaxStep(m_cvode_mem, m_hmax);
    }

    void CVodesIntegrator::integrate(double tout)
    {
        double t;
        int flag;
	flag = CVode(m_cvode_mem, tout, nv(m_y), &t, CV_NORMAL);
	if (flag != CV_SUCCESS)
	  throw CVodesErr(" CVodes error encountered.");
        if (m_np > 0) {
            CVodeGetSens(m_cvode_mem, tout, m_yS);
        }
    }

    double CVodesIntegrator::step(double tout)
    {
        double t;
        int flag;
	flag = CVode(m_cvode_mem, tout, nv(m_y), &t, CV_ONE_STEP);
	if (flag != CV_SUCCESS)
	  throw CVodesErr(" CVodes error encountered.");
        return t;
      }

    int CVodesIntegrator::nEvals() const {
        long int ne;
        CVodeGetNumRhsEvals(m_cvode_mem, &ne);
        return ne;
        //return m_iopt[NFE];
    }

    double CVodesIntegrator::sensitivity(int k, int p) {
        if (k < 0 || k >= m_neq)
            throw CVodesErr("sensitivity: k out of range ("+int2str(p)+")");
        if (p < 0 || p >= m_np)
            throw CVodesErr("sensitivity: p out of range ("+int2str(p)+")");
        return NV_Ith_S(m_yS[p],k);
    }
}

<commit_msg>include config.h<commit_after>/**
 *  @file CVodesIntegrator.cpp
 *
 */

// Copyright 2001  California Institute of Technology
#include "config.h"

#include "CVodesIntegrator.h"
#include "stringUtils.h"

#include <iostream>
using namespace std;

#ifdef SUNDIALS_VERSION_22

#include <sundials_types.h>
#include <sundials_math.h>
#include <cvodes.h>
#include <cvodes_dense.h>
#include <cvodes_diag.h>
#include <cvodes_spgmr.h>
#include <cvodes_band.h>
#include <nvector_serial.h>

#else

#ifdef SUNDIALS_VERSION_23
#include <sundials/sundials_types.h>
#include <sundials/sundials_math.h>
#include <sundials/sundials_nvector.h>
#include <nvector/nvector_serial.h>
#include <cvodes/cvodes.h>
#include <cvodes/cvodes_dense.h>
#include <cvodes/cvodes_diag.h>
#include <cvodes/cvodes_spgmr.h>
#include <cvodes/cvodes_band.h>

#else

unsupported sundials version!

#endif

#endif

inline static N_Vector nv(void* x) {
    return reinterpret_cast<N_Vector>(x);
}

namespace Cantera {

    class FuncData {
    public:
        FuncData(FuncEval* f, int npar = 0) {
            m_pars.resize(npar, 1.0);
            m_func = f;
        }
        virtual ~FuncData() {}
        vector_fp m_pars;
        FuncEval* m_func;
    };
}

extern "C" {

    /**
     *  Function called by cvodes to evaluate ydot given y.  The cvode
     *  integrator allows passing in a void* pointer to access
     *  external data. This pointer is cast to a pointer to a instance
     *  of class FuncEval. The equations to be integrated should be
     *  specified by deriving a class from FuncEval that evaluates the
     *  desired equations.
     *  @ingroup odeGroup
     */
    static int cvodes_rhs(realtype t, N_Vector y, N_Vector ydot,
        void *f_data) {
        double* ydata = NV_DATA_S(y); //N_VDATA(y);
        double* ydotdata = NV_DATA_S(ydot); //N_VDATA(ydot);
        Cantera::FuncData* d = (Cantera::FuncData*)f_data;
        Cantera::FuncEval* f = d->m_func;
        //try {
            if (d->m_pars.size() == 0)
                f->eval(t, ydata, ydotdata, NULL);
            else
                f->eval(t, ydata, ydotdata, DATA_PTR(d->m_pars));
            //}
            //catch (...) {
            //Cantera::showErrors();
            //Cantera::error("Teminating execution");
            //}
            return 0;
    }

}

namespace Cantera {

    /**
     *  Constructor. Default settings: dense jacobian, no user-supplied
     *  Jacobian function, Newton iteration.
     */
    CVodesIntegrator::CVodesIntegrator() : m_neq(0),
                           m_cvode_mem(0),
                           m_t0(0.0),
                           m_y(0),
                           m_abstol(0),
                           m_type(DENSE+NOJAC),
                           m_itol(CV_SS),
                           m_method(CV_BDF),
                           m_iter(CV_NEWTON),
                           m_maxord(0),
                           m_reltol(1.e-9),
                           m_abstols(1.e-15),
                                           m_reltolsens(1.0e-5),
                                           m_abstolsens(1.0e-4),
                           m_nabs(0),
                           m_hmax(0.0),
                                           m_maxsteps(20000), m_np(0),
                                           m_mupper(0), m_mlower(0)
    {
        //m_ropt.resize(OPT_SIZE,0.0);
        //m_iopt = new long[OPT_SIZE];
        //fill(m_iopt, m_iopt+OPT_SIZE,0);
    }

    /// Destructor.
    CVodesIntegrator::~CVodesIntegrator()
    {
        if (m_cvode_mem) {
            if (m_np > 0)
                CVodeSensFree(m_cvode_mem);
            CVodeFree(&m_cvode_mem);
        }
      if (m_y) N_VDestroy_Serial(nv(m_y));
      if (m_abstol) N_VDestroy_Serial(nv(m_abstol));
      delete m_fdata;

      //delete[] m_iopt;
    }

    double& CVodesIntegrator::solution(int k){
        return NV_Ith_S(nv(m_y),k);
    }

    double* CVodesIntegrator::solution(){ return NV_DATA_S(nv(m_y));
    }

    void CVodesIntegrator::setTolerances(double reltol, int n, double* abstol) {
        m_itol = CV_SV;
        m_nabs = n;
        if (n != m_neq) {
            if (m_abstol) N_VDestroy_Serial(nv(m_abstol));
            m_abstol = reinterpret_cast<void*>(N_VNew_Serial(n));
        }
        for (int i=0; i<n; i++) {
            NV_Ith_S(nv(m_abstol), i) = abstol[i];
        }
        m_reltol = reltol;
    }

    void CVodesIntegrator::setTolerances(double reltol, double abstol) {
        m_itol = CV_SS;
        m_reltol = reltol;
        m_abstols = abstol;
    }

    void CVodesIntegrator::setSensitivityTolerances(double reltol, double abstol) {
        m_reltolsens = reltol;
        m_abstolsens = abstol;
    }

    void CVodesIntegrator::setProblemType(int probtype) {
      m_type = probtype;
    }

    void CVodesIntegrator::setMethod(MethodType t) {
        if (t == BDF_Method)
            m_method = CV_BDF;
        else if (t == Adams_Method)
            m_method = CV_ADAMS;
        else
            throw CVodesErr("unknown method");
    }

    void CVodesIntegrator::setMaxStepSize(doublereal hmax) {
        m_hmax = hmax;
        if (m_cvode_mem)
            CVodeSetMaxStep(m_cvode_mem, hmax);
    }

    void CVodesIntegrator::setMinStepSize(doublereal hmin) {
        m_hmin = hmin;
        if (m_cvode_mem)
            CVodeSetMinStep(m_cvode_mem, hmin);
    }

    void CVodesIntegrator::setMaxSteps(int nmax) {
        m_maxsteps = nmax;
        if (m_cvode_mem)
            CVodeSetMaxNumSteps(m_cvode_mem, m_maxsteps);
    }

    void CVodesIntegrator::setIterator(IterType t) {
        if (t == Newton_Iter)
            m_iter = CV_NEWTON;
        else if (t == Functional_Iter)
            m_iter = CV_FUNCTIONAL;
        else
            throw CVodesErr("unknown iterator");
    }

    void CVodesIntegrator::sensInit(double t0, FuncEval& func) {
        m_np = func.nparams();
        long int nv = func.neq();

        doublereal* data;
        int n, j;
        N_Vector y;
        y = N_VNew_Serial(nv);
        m_yS = N_VCloneVectorArray_Serial(m_np, y);
        for (n = 0; n < m_np; n++) {
            data = NV_DATA_S(m_yS[n]);
            for (j = 0; j < nv; j++) {
                data[j] =0.0;
            }
        }

        int flag;
        flag = CVodeSensMalloc(m_cvode_mem, m_np, CV_STAGGERED, m_yS);
        if (flag != CV_SUCCESS)
            throw CVodesErr("Error in CVodeSensMalloc");
        vector_fp atol(m_np, m_abstolsens);
        double rtol = m_reltolsens;
        flag = CVodeSetSensTolerances(m_cvode_mem, CV_SS, rtol, DATA_PTR(atol));
    }

    void CVodesIntegrator::initialize(double t0, FuncEval& func)
    {
        m_neq = func.neq();
        m_t0  = t0;

        if (m_y) {
            N_VDestroy_Serial(nv(m_y));    // free solution vector if already allocated
        }
        m_y = reinterpret_cast<void*>(N_VNew_Serial(m_neq));   // allocate solution vector
        for (int i=0; i<m_neq; i++) {
            NV_Ith_S(nv(m_y), i) = 0.0;
        }
        // check abs tolerance array size
        if (m_itol == CV_SV && m_nabs < m_neq)
            throw CVodesErr("not enough absolute tolerance values specified.");

        func.getInitialConditions(m_t0, m_neq, NV_DATA_S(nv(m_y)));

        if (m_cvode_mem) CVodeFree(&m_cvode_mem);
        m_cvode_mem = CVodeCreate(m_method, m_iter);
        if (!m_cvode_mem) throw CVodesErr("CVodeCreate failed.");

        int flag = 0;
        if (m_itol == CV_SV) {
            // vector atol
            flag = CVodeMalloc(m_cvode_mem, cvodes_rhs, m_t0, nv(m_y), m_itol,
                m_reltol, nv(m_abstol));
        }
        else {
            // scalar atol
            flag = CVodeMalloc(m_cvode_mem, cvodes_rhs, m_t0, nv(m_y), m_itol,
                m_reltol, &m_abstols);
        }
        if (flag != CV_SUCCESS) {
            if (flag == CV_MEM_FAIL) {
                throw CVodesErr("Memory allocation failed.");
            }
            else if (flag == CV_ILL_INPUT) {
                throw CVodesErr("Illegal value for CVodeMalloc input argument.");
            }
            else
                throw CVodesErr("CVodeMalloc failed.");
        }

        if (m_type == DENSE + NOJAC) {
            long int N = m_neq;
            CVDense(m_cvode_mem, N);
        }
        else if (m_type == DIAG) {
            CVDiag(m_cvode_mem);
        }
        else if (m_type == GMRES) {
            CVSpgmr(m_cvode_mem, PREC_NONE, 0);
        }
        else if (m_type == BAND + NOJAC) {
            long int N = m_neq;
            long int nu = m_mupper;
            long int nl = m_mlower;
            CVBand(m_cvode_mem, N, nu, nl);
        }
        else {
            throw CVodesErr("unsupported option");
        }

        // pass a pointer to func in m_data
        m_fdata = new FuncData(&func, func.nparams());

        //m_data = (void*)&func;

        flag = CVodeSetFdata(m_cvode_mem, (void*)m_fdata);
        if (flag != CV_SUCCESS)
            throw CVodesErr("CVodeSetFdata failed.");

        if (func.nparams() > 0) {
            sensInit(t0, func);
            flag = CVodeSetSensParams(m_cvode_mem, DATA_PTR(m_fdata->m_pars),
                NULL, NULL);
        }

        // set options
        if (m_maxord > 0)
            flag = CVodeSetMaxOrd(m_cvode_mem, m_maxord);
        if (m_maxsteps > 0)
            flag = CVodeSetMaxNumSteps(m_cvode_mem, m_maxsteps);
        if (m_hmax > 0)
            flag = CVodeSetMaxStep(m_cvode_mem, m_hmax);
    }

    void CVodesIntegrator::reinitialize(double t0, FuncEval& func)
    {
        m_t0  = t0;
        //try {
            func.getInitialConditions(m_t0, m_neq, NV_DATA_S(nv(m_y)));
            //}
            //catch (CanteraError) {
            //showErrors();
            //error("Teminating execution");
            //}

        int result, flag;
        if (m_itol == CV_SV) {
            result = CVodeReInit(m_cvode_mem, cvodes_rhs, m_t0, nv(m_y),
                m_itol, m_reltol,
                nv(m_abstol));
        }
        else {
            result = CVodeReInit(m_cvode_mem, cvodes_rhs, m_t0, nv(m_y),
                m_itol, m_reltol,
                &m_abstols);
        }
        if (result != CV_SUCCESS)
            throw CVodesErr("CVReInit failed. result = "+int2str(result));

        if (m_type == DENSE + NOJAC) {
            long int N = m_neq;
            CVDense(m_cvode_mem, N);
        }
        else if (m_type == DIAG) {
            CVDiag(m_cvode_mem);
        }
        else if (m_type == BAND + NOJAC) {
            long int N = m_neq;
            long int nu = m_mupper;
            long int nl = m_mlower;
            CVBand(m_cvode_mem, N, nu, nl);
        }
        else if (m_type == GMRES) {
            CVSpgmr(m_cvode_mem, PREC_NONE, 0);
        }
        else {
            throw CVodesErr("unsupported option");
        }

        // set options
        if (m_maxord > 0)
            flag = CVodeSetMaxOrd(m_cvode_mem, m_maxord);
        if (m_maxsteps > 0)
            flag = CVodeSetMaxNumSteps(m_cvode_mem, m_maxsteps);
        if (m_hmax > 0)
            flag = CVodeSetMaxStep(m_cvode_mem, m_hmax);
    }

    void CVodesIntegrator::integrate(double tout)
    {
        double t;
        int flag;
	flag = CVode(m_cvode_mem, tout, nv(m_y), &t, CV_NORMAL);
	if (flag != CV_SUCCESS)
	  throw CVodesErr(" CVodes error encountered.");
        if (m_np > 0) {
            CVodeGetSens(m_cvode_mem, tout, m_yS);
        }
    }

    double CVodesIntegrator::step(double tout)
    {
        double t;
        int flag;
	flag = CVode(m_cvode_mem, tout, nv(m_y), &t, CV_ONE_STEP);
	if (flag != CV_SUCCESS)
	  throw CVodesErr(" CVodes error encountered.");
        return t;
      }

    int CVodesIntegrator::nEvals() const {
        long int ne;
        CVodeGetNumRhsEvals(m_cvode_mem, &ne);
        return ne;
        //return m_iopt[NFE];
    }

    double CVodesIntegrator::sensitivity(int k, int p) {
        if (k < 0 || k >= m_neq)
            throw CVodesErr("sensitivity: k out of range ("+int2str(p)+")");
        if (p < 0 || p >= m_np)
            throw CVodesErr("sensitivity: p out of range ("+int2str(p)+")");
        return NV_Ith_S(m_yS[p],k);
    }
}

<|endoftext|>


<commit_before>#ifdef _WIN32

#include "win_fsnotifier.h"
#include "command.h"

using namespace std;

//
// WatchPoint
//

WatchPoint::WatchPoint(Server* server, size_t eventBufferSize, const wstring& path)
    : pathW(path)
    , path(u16string(path.begin(), path.end()))
    , status(WatchPointStatus::NOT_LISTENING) {
    HANDLE directoryHandle = CreateFileW(
        pathW.c_str(),          // pointer to the file name
        FILE_LIST_DIRECTORY,    // access (read/write) mode
        CREATE_SHARE,           // share mode
        NULL,                   // security descriptor
        OPEN_EXISTING,          // how to create
        CREATE_FLAGS,           // file attributes
        NULL                    // file with attributes to copy
    );
    if (directoryHandle == INVALID_HANDLE_VALUE) {
        throw FileWatcherException("Couldn't add watch", this->path, GetLastError());
    }
    this->directoryHandle = directoryHandle;

    this->server = server;
    this->eventBuffer.reserve(eventBufferSize);
    ZeroMemory(&this->overlapped, sizeof(OVERLAPPED));
    this->overlapped.hEvent = this;
    switch (listen()) {
        case ListenResult::SUCCESS:
            break;
        case ListenResult::DELETED:
            throw FileWatcherException("Couldn't start watching because path is not a directory", this->path);
    }
}

bool WatchPoint::cancel() {
    if (status == WatchPointStatus::LISTENING) {
        logToJava(LogLevel::FINE, "Cancelling %s", utf16ToUtf8String(path).c_str());
        bool cancelled = (bool) CancelIoEx(directoryHandle, &overlapped);
        if (cancelled) {
            status = WatchPointStatus::CANCELLED;
        } else {
            DWORD cancelError = GetLastError();
            close();
            if (cancelError == ERROR_NOT_FOUND) {
                // Do nothing, looks like this is a typical scenario
                logToJava(LogLevel::FINE, "Watch point already finished %s", utf16ToUtf8String(path).c_str());
            } else {
                throw FileWatcherException("Couldn't cancel watch point", path, cancelError);
            }
        }
        return cancelled;
    }
    return false;
}

WatchPoint::~WatchPoint() {
    try {
        cancel();
        SleepEx(0, true);
        close();
    } catch (const exception& ex) {
        logToJava(LogLevel::WARNING, "Couldn't cancel watch point %s: %s", utf16ToUtf8String(path).c_str(), ex.what());
    }
}

static void CALLBACK handleEventCallback(DWORD errorCode, DWORD bytesTransferred, LPOVERLAPPED overlapped) {
    WatchPoint* watchPoint = (WatchPoint*) overlapped->hEvent;
    watchPoint->handleEventsInBuffer(errorCode, bytesTransferred);
}

bool WatchPoint::isValidDirectory() {
    DWORD attrib = GetFileAttributesW(pathW.c_str());

    return (attrib != INVALID_FILE_ATTRIBUTES)
        && ((attrib & FILE_ATTRIBUTE_DIRECTORY) != 0);
}

ListenResult WatchPoint::listen() {
    BOOL success = ReadDirectoryChangesExW(
        directoryHandle,              // handle to directory
        &eventBuffer[0],                   // read results buffer
        (DWORD) eventBuffer.capacity(),    // length of buffer
        TRUE,                         // include children
        EVENT_MASK,                   // filter conditions
        NULL,                         // bytes returned
        &overlapped,                  // overlapped buffer
        &handleEventCallback,         // completion routine
        ReadDirectoryNotifyExtendedInformation
    );
    if (success) {
        status = WatchPointStatus::LISTENING;
        return ListenResult::SUCCESS;
    } else {
        DWORD listenError = GetLastError();
        close();
        if (listenError == ERROR_ACCESS_DENIED && !isValidDirectory()) {
            return ListenResult::DELETED;
        } else {
            throw FileWatcherException("Couldn't start watching", path, listenError);
        }
    }
}

void WatchPoint::close() {
    if (status != WatchPointStatus::FINISHED) {
        try {
            BOOL ret = CloseHandle(directoryHandle);
            if (!ret) {
                logToJava(LogLevel::SEVERE, "Couldn't close handle %p for '%ls': %d", directoryHandle, utf16ToUtf8String(path).c_str(), GetLastError());
            }
        } catch (const exception& ex) {
            // Apparently with debugging enabled CloseHandle() can also throw, see:
            // https://docs.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-closehandle#return-value
            logToJava(LogLevel::SEVERE, "Couldn't close handle %p for '%ls': %s", directoryHandle, utf16ToUtf8String(path).c_str(), ex.what());
        }
        status = WatchPointStatus::FINISHED;
    }
}

void WatchPoint::handleEventsInBuffer(DWORD errorCode, DWORD bytesTransferred) {
    if (errorCode == ERROR_OPERATION_ABORTED) {
        logToJava(LogLevel::FINE, "Finished watching '%s', status = %d", utf16ToUtf8String(path).c_str(), status);
        close();
        return;
    }

    if (status != WatchPointStatus::LISTENING) {
        logToJava(LogLevel::FINE, "Ignoring incoming events for %s as watch-point is not listening (%d bytes, errorCode = %d, status = %d)",
            utf16ToUtf8String(path).c_str(), bytesTransferred, errorCode, status);
        return;
    }
    status = WatchPointStatus::NOT_LISTENING;
    server->handleEvents(this, errorCode, eventBuffer, bytesTransferred);
}

void Server::handleEvents(WatchPoint* watchPoint, DWORD errorCode, const vector<BYTE>& eventBuffer, DWORD bytesTransferred) {
    JNIEnv* env = getThreadEnv();
    const u16string& path = watchPoint->path;
    const wstring& pathW = watchPoint->pathW;

    try {
        if (errorCode != ERROR_SUCCESS) {
            if (errorCode == ERROR_ACCESS_DENIED && !watchPoint->isValidDirectory()) {
                reportChangeEvent(env, ChangeType::REMOVED, path);
                watchPoint->close();
                return;
            } else {
                throw FileWatcherException("Error received when handling events", path, errorCode);
            }
        }

        if (shouldTerminate) {
            logToJava(LogLevel::FINE, "Ignoring incoming events for %s because server is terminating (%d bytes, status = %d)",
                utf16ToUtf8String(path).c_str(), bytesTransferred, watchPoint->status);
            return;
        }

        if (bytesTransferred == 0) {
            // This is what the documentation has to say about a zero-length dataset:
            //
            //     If the number of bytes transferred is zero, the eventBuffer was either too large
            //     for the system to allocate or too small to provide detailed information on
            //     all the changes that occurred in the directory or subtree. In this case,
            //     you should compute the changes by enumerating the directory or subtree.
            //
            // (See https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-readdirectorychangesw)
            //
            // We'll handle this as a simple overflow and report it as such.
            reportOverflow(env, path);
        } else {
            int index = 0;
            for (;;) {
                FILE_NOTIFY_EXTENDED_INFORMATION* current = (FILE_NOTIFY_EXTENDED_INFORMATION*) &eventBuffer[index];
                handleEvent(env, pathW, current);
                if (current->NextEntryOffset == 0) {
                    break;
                }
                index += current->NextEntryOffset;
            }
        }

        switch (watchPoint->listen()) {
            case ListenResult::SUCCESS:
                break;
            case ListenResult::DELETED:
                logToJava(LogLevel::FINE, "Watched directory removed for %s", utf16ToUtf8String(path).c_str());
                reportChangeEvent(env, ChangeType::REMOVED, path);
                break;
        }
    } catch (const exception& ex) {
        reportFailure(env, ex);
    }
}

bool isAbsoluteLocalPath(const wstring& path) {
    if (path.length() < 3) {
        return false;
    }
    return ((L'a' <= path[0] && path[0] <= L'z') || (L'A' <= path[0] && path[0] <= L'Z'))
        && path[1] == L':'
        && path[2] == L'\\';
}

bool isAbsoluteUncPath(const wstring& path) {
    if (path.length() < 3) {
        return false;
    }
    return path[0] == L'\\' && path[1] == L'\\';
}

bool isLongPath(const wstring& path) {
    return path.length() >= 4 && path.substr(0, 4) == L"\\\\?\\";
}

bool isUncLongPath(const wstring& path) {
    return path.length() >= 8 && path.substr(0, 8) == L"\\\\?\\UNC\\";
}

// TODO How can this be done nicer, wihtout both unnecessary copy and in-place mutation?
void convertToLongPathIfNeeded(wstring& path) {
    // Technically, this should be MAX_PATH (i.e. 260), except some Win32 API related
    // to working with directory paths are actually limited to 240. It is just
    // safer/simpler to cover both cases in one code path.
    if (path.length() <= 240) {
        return;
    }

    // It is already a long path, nothing to do here
    if (isLongPath(path)) {
        return;
    }

    if (isAbsoluteLocalPath(path)) {
        // Format: C:\... -> \\?\C:\...
        path.insert(0, L"\\\\?\\");
    } else if (isAbsoluteUncPath(path)) {
        // In this case, we need to skip the first 2 characters:
        // Format: \\server\share\... -> \\?\UNC\server\share\...
        path.erase(0, 2);
        path.insert(0, L"\\\\?\\UNC\\");
    } else {
        // It is some sort of unknown format, don't mess with it
    }
}

void Server::handleEvent(JNIEnv* env, const wstring& watchedPathW, FILE_NOTIFY_EXTENDED_INFORMATION* info) {
    wstring changedPathW = wstring(info->FileName, 0, info->FileNameLength / sizeof(wchar_t));
    if (!changedPathW.empty()) {
        changedPathW.insert(0, 1, L'\\');
    }
    changedPathW.insert(0, watchedPathW);
    // TODO Remove long prefix for path once?
    if (isLongPath(changedPathW)) {
        if (isUncLongPath(changedPathW)) {
            changedPathW.erase(0, 8).insert(0, L"\\\\");
        } else {
            changedPathW.erase(0, 4);
        }
    }
    u16string changedPath(changedPathW.begin(), changedPathW.end());

    logToJava(LogLevel::FINE, "Change detected: 0x%x '%s'", info->Action, utf16ToUtf8String(changedPath).c_str());

    ChangeType type;
    if (info->Action == FILE_ACTION_ADDED || info->Action == FILE_ACTION_RENAMED_NEW_NAME) {
        type = ChangeType::CREATED;
    } else if (info->Action == FILE_ACTION_REMOVED || info->Action == FILE_ACTION_RENAMED_OLD_NAME) {
        type = ChangeType::REMOVED;
    } else if (info->Action == FILE_ACTION_MODIFIED) {
        if (info->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
            // Ignore MODIFIED events on directories
            logToJava(LogLevel::FINE, "Ignored MODIFIED event on directory", nullptr);
            return;
        }
        type = ChangeType::MODIFIED;
    } else {
        logToJava(LogLevel::WARNING, "Unknown event 0x%x for %s", info->Action, utf16ToUtf8String(changedPath).c_str());
        reportUnknownEvent(env, changedPath);
        return;
    }

    reportChangeEvent(env, type, changedPath);
}

//
// Server
//

Server::Server(JNIEnv* env, size_t eventBufferSize, long commandTimeoutInMillis, jobject watcherCallback)
    : AbstractServer(env, watcherCallback)
    , eventBufferSize(eventBufferSize)
    , commandTimeoutInMillis(commandTimeoutInMillis) {
}

void Server::initializeRunLoop() {
    // For some reason GetCurrentThread() returns a thread that doesn't accept APCs
    // so we need to use OpenThread() instead.
    threadHandle = OpenThread(
        THREAD_ALL_ACCESS,      // dwDesiredAccess
        false,                  // bInheritHandle
        GetCurrentThreadId()    // dwThreadId
    );
    if (threadHandle == NULL) {
        throw FileWatcherException("Couldn't open current thread", GetLastError());
    }
}

void Server::shutdownRunLoop() {
    executeOnRunLoop([this]() {
        shouldTerminate = true;
        return true;
    });
}

void Server::runLoop() {
    while (!shouldTerminate) {
        SleepEx(INFINITE, true);
    }

    // We have received termination, cancel all watchers
    logToJava(LogLevel::FINE, "Finished with run loop, now cancelling remaining watch points", NULL);
    for (auto& it : watchPoints) {
        auto& watchPoint = it.second;
        if (watchPoint.status == WatchPointStatus::LISTENING) {
            try {
                watchPoint.cancel();
            } catch (const exception& ex) {
                logToJava(LogLevel::SEVERE, "%s", ex.what());
            }
        }
    }

    logToJava(LogLevel::FINE, "Waiting for any pending watch points to abort completely", NULL);
    SleepEx(0, true);

    // Warn about  any unfinished watchpoints
    for (auto& it : watchPoints) {
        auto& watchPoint = it.second;
        switch (watchPoint.status) {
            case WatchPointStatus::NOT_LISTENING:
            case WatchPointStatus::FINISHED:
                break;
            default:
                logToJava(LogLevel::WARNING, "Watch point %s did not finish before termination timeout (status = %d)",
                    utf16ToUtf8String(watchPoint.path).c_str(), watchPoint.status);
                break;
        }
    }

    CloseHandle(threadHandle);
}

static void CALLBACK executeOnRunLoopCallback(_In_ ULONG_PTR info) {
    Command* command = (Command*) info;
    command->executeInsideRunLoop();
}

bool Server::executeOnRunLoop(function<bool()> function) {
    Command command(function);
    return command.execute(commandTimeoutInMillis, [this](Command* command) {
        DWORD ret = QueueUserAPC(executeOnRunLoopCallback, threadHandle, (ULONG_PTR) command);
        if (ret == 0) {
            throw FileWatcherException("Received error while queuing APC", GetLastError());
        }
    });
}

void Server::registerPaths(const vector<u16string>& paths) {
    executeOnRunLoop([this, paths]() {
        for (auto& path : paths) {
            registerPath(path);
        }
        return true;
    });
}

bool Server::unregisterPaths(const vector<u16string>& paths) {
    return executeOnRunLoop([this, paths]() {
        bool success = true;
        for (auto& path : paths) {
            success &= unregisterPath(path);
        }
        return success;
    });
}

void Server::registerPath(const u16string& path) {
    wstring longPathW(path.begin(), path.end());
    convertToLongPathIfNeeded(longPathW);
    auto it = watchPoints.find(longPathW);
    if (it != watchPoints.end()) {
        if (it->second.status != WatchPointStatus::FINISHED) {
            throw FileWatcherException("Already watching path", path);
        }
        watchPoints.erase(it);
    }
    watchPoints.emplace(piecewise_construct,
        forward_as_tuple(longPathW),
        forward_as_tuple(this, eventBufferSize, longPathW));
}

bool Server::unregisterPath(const u16string& path) {
    wstring longPathW(path.begin(), path.end());
    convertToLongPathIfNeeded(longPathW);
    if (watchPoints.erase(longPathW) == 0) {
        logToJava(LogLevel::INFO, "Path is not watched: %s", utf16ToUtf8String(path).c_str());
        return false;
    }
    return true;
}

//
// JNI calls
//

JNIEXPORT jobject JNICALL
Java_net_rubygrapefruit_platform_internal_jni_WindowsFileEventFunctions_startWatcher0(JNIEnv* env, jclass target, jint eventBufferSize, jlong commandTimeoutInMillis, jobject javaCallback) {
    return wrapServer(env, new Server(env, eventBufferSize, (long) commandTimeoutInMillis, javaCallback));
}

#endif
<commit_msg>Simplify WatchPoint constructor<commit_after>#ifdef _WIN32

#include "win_fsnotifier.h"
#include "command.h"

using namespace std;

//
// WatchPoint
//

WatchPoint::WatchPoint(Server* server, size_t eventBufferSize, const wstring& path)
    : pathW(path)
    , path(u16string(path.begin(), path.end()))
    , status(WatchPointStatus::NOT_LISTENING)
    , server(server) {
    HANDLE directoryHandle = CreateFileW(
        pathW.c_str(),          // pointer to the file name
        FILE_LIST_DIRECTORY,    // access (read/write) mode
        CREATE_SHARE,           // share mode
        NULL,                   // security descriptor
        OPEN_EXISTING,          // how to create
        CREATE_FLAGS,           // file attributes
        NULL                    // file with attributes to copy
    );
    if (directoryHandle == INVALID_HANDLE_VALUE) {
        throw FileWatcherException("Couldn't add watch", this->path, GetLastError());
    }
    this->directoryHandle = directoryHandle;
    this->eventBuffer.reserve(eventBufferSize);
    ZeroMemory(&this->overlapped, sizeof(OVERLAPPED));
    this->overlapped.hEvent = this;
    switch (listen()) {
        case ListenResult::SUCCESS:
            break;
        case ListenResult::DELETED:
            throw FileWatcherException("Couldn't start watching because path is not a directory", this->path);
    }
}

bool WatchPoint::cancel() {
    if (status == WatchPointStatus::LISTENING) {
        logToJava(LogLevel::FINE, "Cancelling %s", utf16ToUtf8String(path).c_str());
        bool cancelled = (bool) CancelIoEx(directoryHandle, &overlapped);
        if (cancelled) {
            status = WatchPointStatus::CANCELLED;
        } else {
            DWORD cancelError = GetLastError();
            close();
            if (cancelError == ERROR_NOT_FOUND) {
                // Do nothing, looks like this is a typical scenario
                logToJava(LogLevel::FINE, "Watch point already finished %s", utf16ToUtf8String(path).c_str());
            } else {
                throw FileWatcherException("Couldn't cancel watch point", path, cancelError);
            }
        }
        return cancelled;
    }
    return false;
}

WatchPoint::~WatchPoint() {
    try {
        cancel();
        SleepEx(0, true);
        close();
    } catch (const exception& ex) {
        logToJava(LogLevel::WARNING, "Couldn't cancel watch point %s: %s", utf16ToUtf8String(path).c_str(), ex.what());
    }
}

static void CALLBACK handleEventCallback(DWORD errorCode, DWORD bytesTransferred, LPOVERLAPPED overlapped) {
    WatchPoint* watchPoint = (WatchPoint*) overlapped->hEvent;
    watchPoint->handleEventsInBuffer(errorCode, bytesTransferred);
}

bool WatchPoint::isValidDirectory() {
    DWORD attrib = GetFileAttributesW(pathW.c_str());

    return (attrib != INVALID_FILE_ATTRIBUTES)
        && ((attrib & FILE_ATTRIBUTE_DIRECTORY) != 0);
}

ListenResult WatchPoint::listen() {
    BOOL success = ReadDirectoryChangesExW(
        directoryHandle,              // handle to directory
        &eventBuffer[0],                   // read results buffer
        (DWORD) eventBuffer.capacity(),    // length of buffer
        TRUE,                         // include children
        EVENT_MASK,                   // filter conditions
        NULL,                         // bytes returned
        &overlapped,                  // overlapped buffer
        &handleEventCallback,         // completion routine
        ReadDirectoryNotifyExtendedInformation
    );
    if (success) {
        status = WatchPointStatus::LISTENING;
        return ListenResult::SUCCESS;
    } else {
        DWORD listenError = GetLastError();
        close();
        if (listenError == ERROR_ACCESS_DENIED && !isValidDirectory()) {
            return ListenResult::DELETED;
        } else {
            throw FileWatcherException("Couldn't start watching", path, listenError);
        }
    }
}

void WatchPoint::close() {
    if (status != WatchPointStatus::FINISHED) {
        try {
            BOOL ret = CloseHandle(directoryHandle);
            if (!ret) {
                logToJava(LogLevel::SEVERE, "Couldn't close handle %p for '%ls': %d", directoryHandle, utf16ToUtf8String(path).c_str(), GetLastError());
            }
        } catch (const exception& ex) {
            // Apparently with debugging enabled CloseHandle() can also throw, see:
            // https://docs.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-closehandle#return-value
            logToJava(LogLevel::SEVERE, "Couldn't close handle %p for '%ls': %s", directoryHandle, utf16ToUtf8String(path).c_str(), ex.what());
        }
        status = WatchPointStatus::FINISHED;
    }
}

void WatchPoint::handleEventsInBuffer(DWORD errorCode, DWORD bytesTransferred) {
    if (errorCode == ERROR_OPERATION_ABORTED) {
        logToJava(LogLevel::FINE, "Finished watching '%s', status = %d", utf16ToUtf8String(path).c_str(), status);
        close();
        return;
    }

    if (status != WatchPointStatus::LISTENING) {
        logToJava(LogLevel::FINE, "Ignoring incoming events for %s as watch-point is not listening (%d bytes, errorCode = %d, status = %d)",
            utf16ToUtf8String(path).c_str(), bytesTransferred, errorCode, status);
        return;
    }
    status = WatchPointStatus::NOT_LISTENING;
    server->handleEvents(this, errorCode, eventBuffer, bytesTransferred);
}

void Server::handleEvents(WatchPoint* watchPoint, DWORD errorCode, const vector<BYTE>& eventBuffer, DWORD bytesTransferred) {
    JNIEnv* env = getThreadEnv();
    const u16string& path = watchPoint->path;
    const wstring& pathW = watchPoint->pathW;

    try {
        if (errorCode != ERROR_SUCCESS) {
            if (errorCode == ERROR_ACCESS_DENIED && !watchPoint->isValidDirectory()) {
                reportChangeEvent(env, ChangeType::REMOVED, path);
                watchPoint->close();
                return;
            } else {
                throw FileWatcherException("Error received when handling events", path, errorCode);
            }
        }

        if (shouldTerminate) {
            logToJava(LogLevel::FINE, "Ignoring incoming events for %s because server is terminating (%d bytes, status = %d)",
                utf16ToUtf8String(path).c_str(), bytesTransferred, watchPoint->status);
            return;
        }

        if (bytesTransferred == 0) {
            // This is what the documentation has to say about a zero-length dataset:
            //
            //     If the number of bytes transferred is zero, the eventBuffer was either too large
            //     for the system to allocate or too small to provide detailed information on
            //     all the changes that occurred in the directory or subtree. In this case,
            //     you should compute the changes by enumerating the directory or subtree.
            //
            // (See https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-readdirectorychangesw)
            //
            // We'll handle this as a simple overflow and report it as such.
            reportOverflow(env, path);
        } else {
            int index = 0;
            for (;;) {
                FILE_NOTIFY_EXTENDED_INFORMATION* current = (FILE_NOTIFY_EXTENDED_INFORMATION*) &eventBuffer[index];
                handleEvent(env, pathW, current);
                if (current->NextEntryOffset == 0) {
                    break;
                }
                index += current->NextEntryOffset;
            }
        }

        switch (watchPoint->listen()) {
            case ListenResult::SUCCESS:
                break;
            case ListenResult::DELETED:
                logToJava(LogLevel::FINE, "Watched directory removed for %s", utf16ToUtf8String(path).c_str());
                reportChangeEvent(env, ChangeType::REMOVED, path);
                break;
        }
    } catch (const exception& ex) {
        reportFailure(env, ex);
    }
}

bool isAbsoluteLocalPath(const wstring& path) {
    if (path.length() < 3) {
        return false;
    }
    return ((L'a' <= path[0] && path[0] <= L'z') || (L'A' <= path[0] && path[0] <= L'Z'))
        && path[1] == L':'
        && path[2] == L'\\';
}

bool isAbsoluteUncPath(const wstring& path) {
    if (path.length() < 3) {
        return false;
    }
    return path[0] == L'\\' && path[1] == L'\\';
}

bool isLongPath(const wstring& path) {
    return path.length() >= 4 && path.substr(0, 4) == L"\\\\?\\";
}

bool isUncLongPath(const wstring& path) {
    return path.length() >= 8 && path.substr(0, 8) == L"\\\\?\\UNC\\";
}

// TODO How can this be done nicer, wihtout both unnecessary copy and in-place mutation?
void convertToLongPathIfNeeded(wstring& path) {
    // Technically, this should be MAX_PATH (i.e. 260), except some Win32 API related
    // to working with directory paths are actually limited to 240. It is just
    // safer/simpler to cover both cases in one code path.
    if (path.length() <= 240) {
        return;
    }

    // It is already a long path, nothing to do here
    if (isLongPath(path)) {
        return;
    }

    if (isAbsoluteLocalPath(path)) {
        // Format: C:\... -> \\?\C:\...
        path.insert(0, L"\\\\?\\");
    } else if (isAbsoluteUncPath(path)) {
        // In this case, we need to skip the first 2 characters:
        // Format: \\server\share\... -> \\?\UNC\server\share\...
        path.erase(0, 2);
        path.insert(0, L"\\\\?\\UNC\\");
    } else {
        // It is some sort of unknown format, don't mess with it
    }
}

void Server::handleEvent(JNIEnv* env, const wstring& watchedPathW, FILE_NOTIFY_EXTENDED_INFORMATION* info) {
    wstring changedPathW = wstring(info->FileName, 0, info->FileNameLength / sizeof(wchar_t));
    if (!changedPathW.empty()) {
        changedPathW.insert(0, 1, L'\\');
    }
    changedPathW.insert(0, watchedPathW);
    // TODO Remove long prefix for path once?
    if (isLongPath(changedPathW)) {
        if (isUncLongPath(changedPathW)) {
            changedPathW.erase(0, 8).insert(0, L"\\\\");
        } else {
            changedPathW.erase(0, 4);
        }
    }
    u16string changedPath(changedPathW.begin(), changedPathW.end());

    logToJava(LogLevel::FINE, "Change detected: 0x%x '%s'", info->Action, utf16ToUtf8String(changedPath).c_str());

    ChangeType type;
    if (info->Action == FILE_ACTION_ADDED || info->Action == FILE_ACTION_RENAMED_NEW_NAME) {
        type = ChangeType::CREATED;
    } else if (info->Action == FILE_ACTION_REMOVED || info->Action == FILE_ACTION_RENAMED_OLD_NAME) {
        type = ChangeType::REMOVED;
    } else if (info->Action == FILE_ACTION_MODIFIED) {
        if (info->FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
            // Ignore MODIFIED events on directories
            logToJava(LogLevel::FINE, "Ignored MODIFIED event on directory", nullptr);
            return;
        }
        type = ChangeType::MODIFIED;
    } else {
        logToJava(LogLevel::WARNING, "Unknown event 0x%x for %s", info->Action, utf16ToUtf8String(changedPath).c_str());
        reportUnknownEvent(env, changedPath);
        return;
    }

    reportChangeEvent(env, type, changedPath);
}

//
// Server
//

Server::Server(JNIEnv* env, size_t eventBufferSize, long commandTimeoutInMillis, jobject watcherCallback)
    : AbstractServer(env, watcherCallback)
    , eventBufferSize(eventBufferSize)
    , commandTimeoutInMillis(commandTimeoutInMillis) {
}

void Server::initializeRunLoop() {
    // For some reason GetCurrentThread() returns a thread that doesn't accept APCs
    // so we need to use OpenThread() instead.
    threadHandle = OpenThread(
        THREAD_ALL_ACCESS,      // dwDesiredAccess
        false,                  // bInheritHandle
        GetCurrentThreadId()    // dwThreadId
    );
    if (threadHandle == NULL) {
        throw FileWatcherException("Couldn't open current thread", GetLastError());
    }
}

void Server::shutdownRunLoop() {
    executeOnRunLoop([this]() {
        shouldTerminate = true;
        return true;
    });
}

void Server::runLoop() {
    while (!shouldTerminate) {
        SleepEx(INFINITE, true);
    }

    // We have received termination, cancel all watchers
    logToJava(LogLevel::FINE, "Finished with run loop, now cancelling remaining watch points", NULL);
    for (auto& it : watchPoints) {
        auto& watchPoint = it.second;
        if (watchPoint.status == WatchPointStatus::LISTENING) {
            try {
                watchPoint.cancel();
            } catch (const exception& ex) {
                logToJava(LogLevel::SEVERE, "%s", ex.what());
            }
        }
    }

    logToJava(LogLevel::FINE, "Waiting for any pending watch points to abort completely", NULL);
    SleepEx(0, true);

    // Warn about  any unfinished watchpoints
    for (auto& it : watchPoints) {
        auto& watchPoint = it.second;
        switch (watchPoint.status) {
            case WatchPointStatus::NOT_LISTENING:
            case WatchPointStatus::FINISHED:
                break;
            default:
                logToJava(LogLevel::WARNING, "Watch point %s did not finish before termination timeout (status = %d)",
                    utf16ToUtf8String(watchPoint.path).c_str(), watchPoint.status);
                break;
        }
    }

    CloseHandle(threadHandle);
}

static void CALLBACK executeOnRunLoopCallback(_In_ ULONG_PTR info) {
    Command* command = (Command*) info;
    command->executeInsideRunLoop();
}

bool Server::executeOnRunLoop(function<bool()> function) {
    Command command(function);
    return command.execute(commandTimeoutInMillis, [this](Command* command) {
        DWORD ret = QueueUserAPC(executeOnRunLoopCallback, threadHandle, (ULONG_PTR) command);
        if (ret == 0) {
            throw FileWatcherException("Received error while queuing APC", GetLastError());
        }
    });
}

void Server::registerPaths(const vector<u16string>& paths) {
    executeOnRunLoop([this, paths]() {
        for (auto& path : paths) {
            registerPath(path);
        }
        return true;
    });
}

bool Server::unregisterPaths(const vector<u16string>& paths) {
    return executeOnRunLoop([this, paths]() {
        bool success = true;
        for (auto& path : paths) {
            success &= unregisterPath(path);
        }
        return success;
    });
}

void Server::registerPath(const u16string& path) {
    wstring longPathW(path.begin(), path.end());
    convertToLongPathIfNeeded(longPathW);
    auto it = watchPoints.find(longPathW);
    if (it != watchPoints.end()) {
        if (it->second.status != WatchPointStatus::FINISHED) {
            throw FileWatcherException("Already watching path", path);
        }
        watchPoints.erase(it);
    }
    watchPoints.emplace(piecewise_construct,
        forward_as_tuple(longPathW),
        forward_as_tuple(this, eventBufferSize, longPathW));
}

bool Server::unregisterPath(const u16string& path) {
    wstring longPathW(path.begin(), path.end());
    convertToLongPathIfNeeded(longPathW);
    if (watchPoints.erase(longPathW) == 0) {
        logToJava(LogLevel::INFO, "Path is not watched: %s", utf16ToUtf8String(path).c_str());
        return false;
    }
    return true;
}

//
// JNI calls
//

JNIEXPORT jobject JNICALL
Java_net_rubygrapefruit_platform_internal_jni_WindowsFileEventFunctions_startWatcher0(JNIEnv* env, jclass target, jint eventBufferSize, jlong commandTimeoutInMillis, jobject javaCallback) {
    return wrapServer(env, new Server(env, eventBufferSize, (long) commandTimeoutInMillis, javaCallback));
}

#endif
<|endoftext|>


<commit_before>/*=========================================================================

Program:   Medical Imaging & Interaction Toolkit
Module:    $RCSfile$
Language:  C++
Date:      $Date$
Version:   $Revision$

Copyright (c) German Cancer Research Center, Division of Medical and
Biological Informatics. All rights reserved.
See MITKCopyright.txt or http://www.mitk.org/copyright.html for details.

This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.  See the above copyright notices for more information.

=========================================================================*/

#include "QmitkRenderWindow.h"
#include "mitkOpenGLRenderer.h"
#include "mitkRenderWindow.h"
#include <qcursor.h>
#include "mitkDisplayPositionEvent.h"
//##ModelId=3E1EB4410304
QmitkRenderWindow::QmitkRenderWindow(mitk::BaseRenderer* renderer, QGLFormat glf, QWidget *parent, const char *name)
  : QGLWidget(glf, parent, name), mitk::RenderWindow(name, renderer), m_InitNeeded(false), m_ResizeNeeded(false), m_InResize(false)
{
  InitRenderer();
  setFocusPolicy(QWidget::StrongFocus);
  setMouseTracking(true);
}

QmitkRenderWindow::QmitkRenderWindow(QGLFormat glf, QWidget *parent, const char *name)
  : QGLWidget(glf, parent, name), mitk::RenderWindow(name, NULL), m_InitNeeded(false), m_ResizeNeeded(false), m_InResize(false)
{
  InitRenderer();
  setFocusPolicy(QWidget::StrongFocus);
  setMouseTracking(true);
}

//##ModelId=3E1EB4410318
QmitkRenderWindow::QmitkRenderWindow(mitk::BaseRenderer* renderer, QWidget *parent, const char *name)
: QGLWidget(parent, name), mitk::RenderWindow(name, renderer), m_InitNeeded(false), m_ResizeNeeded(false), m_InResize(false)
{
  InitRenderer();
  setFocusPolicy(QWidget::StrongFocus);
  setMouseTracking(true);
}

QmitkRenderWindow::QmitkRenderWindow(QWidget *parent, const char *name)
: QGLWidget(parent, name), mitk::RenderWindow(name, NULL), m_InitNeeded(false), m_ResizeNeeded(false), m_InResize(false)
{
  InitRenderer();
  setFocusPolicy(QWidget::StrongFocus);
  setMouseTracking(true);
}

//##ModelId=3E1EB441032C
QmitkRenderWindow::~QmitkRenderWindow()
{

}

void QmitkRenderWindow::InitRenderer()
{
  m_InitNeeded = true;
  m_ResizeNeeded = true;

  mitk::RenderWindow::InitRenderer();

  this->setAutoBufferSwap( false );
}

/*!
\brief Initialize the OpenGL Window
*/
//##ModelId=3E33145903C8
void QmitkRenderWindow::initializeGL()
{
  if(m_Renderer.IsNotNull())
    m_Renderer->Initialize();
}

/*!
\brief Resize the OpenGL Window
*/
//##ModelId=3E33145A001C
void QmitkRenderWindow::resizeGL( int w, int h )
{
  if(m_InResize) //@FIXME CRITICAL probably related to VtkSizeBug
    return;
  m_InResize = true;
  if(QGLWidget::isVisible())
  {
    if(m_Renderer.IsNotNull())
      m_Renderer->Resize(w, h);
    updateGL();
  }
  m_InResize = false;
}

/*!
\brief Render the scene
*/
//##ModelId=3E3314590396
void QmitkRenderWindow::paintGL( )
{
  // Get the native window ID and pass it
  // to the m_Renderer
  // before we render for the first time...
  if(m_InitNeeded)
  {
    WId nId = winId();
    SetWindowId( (void*) nId );
    m_InitNeeded = false;
  }
  // rendering when not visible might cause serious problems (program may hang)
  if(QGLWidget::isVisible())
  {
    if(m_Renderer.IsNotNull())
      m_Renderer->Paint();
  }
}

void QmitkRenderWindow::showEvent ( QShowEvent * )
{
  // when the widget becomes visible the first time, we need to tell vtk the window size
  if(m_ResizeNeeded)
  {
    m_ResizeNeeded=false;
    if(m_Renderer.IsNotNull())
      m_Renderer->InitSize(width(),height());
  }
}

//##ModelId=3E3D1D4A00A5
void QmitkRenderWindow::MakeCurrent()
{
  makeCurrent();
}

void QmitkRenderWindow::SwapBuffers()
{
  swapBuffers();
};

bool QmitkRenderWindow::IsSharing () const
{
  return isSharing();
}

//##ModelId=3E6D5DD40306
void QmitkRenderWindow::mousePressEvent(QMouseEvent *me)
{
  QGLWidget::mousePressEvent(me);
  if (m_Renderer.IsNotNull())
  {
    Point2D p; p[0]=me->x(); p[1]=me->y();
    mitk::MouseEvent event(m_Renderer, me->type(), me->button(), me->state(), Qt::Key_unknown, p);
    m_Renderer->MousePressEvent(&event);
  }
}

//##ModelId=3E6D5DD4032E
void QmitkRenderWindow::mouseReleaseEvent(QMouseEvent *me)
{
  QGLWidget::mouseReleaseEvent(me);
  if (m_Renderer.IsNotNull())
  {
    Point2D p; p[0]=me->x(); p[1]=me->y();
    mitk::MouseEvent event(m_Renderer, me->type(), me->button(), me->state(), Qt::Key_unknown, p);
    m_Renderer->MouseReleaseEvent(&event);
  }
}

//##ModelId=3E6D5DD40356
void QmitkRenderWindow::mouseMoveEvent(QMouseEvent *me)
{
  QGLWidget::mouseMoveEvent(me);
  if (m_Renderer.IsNotNull()) {
    Point2D p; p[0]=me->x(); p[1]=me->y();
    mitk::MouseEvent event(m_Renderer, me->type(), me->button(), me->state(), Qt::Key_unknown, p);
    m_Renderer->MouseMoveEvent(&event);
  }
}

void QmitkRenderWindow::wheelEvent(QWheelEvent *we)
{
  we->ignore();
  /*  QGLWidget::wheelEvent(we);
  if (m_Renderer.IsNotNull())
    m_Renderer->WheelEvent(we); */
}

//##ModelId=3E6D5DD40388
void QmitkRenderWindow::keyPressEvent(QKeyEvent *ke)
{
  if (m_Renderer.IsNotNull())
  {
    QPoint cp = mapFromGlobal(QCursor::pos());
    mitk::KeyEvent mke(ke->type(), ke->key(), ke->ascii(), ke->state(), ke->text().ascii(), ke->isAutoRepeat(), ke->count(), cp.x(), cp.y(), QCursor::pos().x(), QCursor::pos().y());
    m_Renderer->KeyPressEvent(&mke);
    if(mke.isAccepted())
      ke->accept();
  }
}

//##ModelId=3EF59AD202B7
//check for update
void QmitkRenderWindow::Update()
{
  update();
};

//force an update
void QmitkRenderWindow::Repaint()
{
  //hide();//strongest update! but flickering widgets in zoom!
  //show();
  //updateGL();//not strong enough
  repaint();
};

QSize QmitkRenderWindow::minimumSizeHint () const
{
  return QSize(100, 100);
}

QSizePolicy QmitkRenderWindow::sizePolicy() const
{
  return QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding, 0, 0);
}

QSize QmitkRenderWindow::sizeHint () const
{
  return QSize(100, 100);
}

//##ModelId=3E6D5DD403B0
void QmitkRenderWindow::focusInEvent(QFocusEvent*)  {};
//##ModelId=3E6D5DD403E2
void QmitkRenderWindow::focusOutEvent(QFocusEvent*) {};

//We have to put this in a file containing a class that is directly used
//somewhere. Otherwise, e.g. when put in VtkRenderWindowInteractor.cpp,
//it is removed by the linker.
#include "VtkQRenderWindowInteractor.h"
VtkQRenderWindowInteractorFactory vtkQRenderWindowInteractorFactory;
<commit_msg>FIX: compile error<commit_after>/*=========================================================================

Program:   Medical Imaging & Interaction Toolkit
Module:    $RCSfile$
Language:  C++
Date:      $Date$
Version:   $Revision$

Copyright (c) German Cancer Research Center, Division of Medical and
Biological Informatics. All rights reserved.
See MITKCopyright.txt or http://www.mitk.org/copyright.html for details.

This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE.  See the above copyright notices for more information.

=========================================================================*/

#include "QmitkRenderWindow.h"
#include "mitkOpenGLRenderer.h"
#include "mitkRenderWindow.h"
#include <qcursor.h>
#include "mitkDisplayPositionEvent.h"
//##ModelId=3E1EB4410304
QmitkRenderWindow::QmitkRenderWindow(mitk::BaseRenderer* renderer, QGLFormat glf, QWidget *parent, const char *name)
  : QGLWidget(glf, parent, name), mitk::RenderWindow(name, renderer), m_InitNeeded(false), m_ResizeNeeded(false), m_InResize(false)
{
  InitRenderer();
  setFocusPolicy(QWidget::StrongFocus);
  setMouseTracking(true);
}

QmitkRenderWindow::QmitkRenderWindow(QGLFormat glf, QWidget *parent, const char *name)
  : QGLWidget(glf, parent, name), mitk::RenderWindow(name, NULL), m_InitNeeded(false), m_ResizeNeeded(false), m_InResize(false)
{
  InitRenderer();
  setFocusPolicy(QWidget::StrongFocus);
  setMouseTracking(true);
}

//##ModelId=3E1EB4410318
QmitkRenderWindow::QmitkRenderWindow(mitk::BaseRenderer* renderer, QWidget *parent, const char *name)
: QGLWidget(parent, name), mitk::RenderWindow(name, renderer), m_InitNeeded(false), m_ResizeNeeded(false), m_InResize(false)
{
  InitRenderer();
  setFocusPolicy(QWidget::StrongFocus);
  setMouseTracking(true);
}

QmitkRenderWindow::QmitkRenderWindow(QWidget *parent, const char *name)
: QGLWidget(parent, name), mitk::RenderWindow(name, NULL), m_InitNeeded(false), m_ResizeNeeded(false), m_InResize(false)
{
  InitRenderer();
  setFocusPolicy(QWidget::StrongFocus);
  setMouseTracking(true);
}

//##ModelId=3E1EB441032C
QmitkRenderWindow::~QmitkRenderWindow()
{

}

void QmitkRenderWindow::InitRenderer()
{
  m_InitNeeded = true;
  m_ResizeNeeded = true;

  mitk::RenderWindow::InitRenderer();

  this->setAutoBufferSwap( false );
}

/*!
\brief Initialize the OpenGL Window
*/
//##ModelId=3E33145903C8
void QmitkRenderWindow::initializeGL()
{
  if(m_Renderer.IsNotNull())
    m_Renderer->Initialize();
}

/*!
\brief Resize the OpenGL Window
*/
//##ModelId=3E33145A001C
void QmitkRenderWindow::resizeGL( int w, int h )
{
  if(m_InResize) //@FIXME CRITICAL probably related to VtkSizeBug
    return;
  m_InResize = true;
  if(QGLWidget::isVisible())
  {
    if(m_Renderer.IsNotNull())
      m_Renderer->Resize(w, h);
    updateGL();
  }
  m_InResize = false;
}

/*!
\brief Render the scene
*/
//##ModelId=3E3314590396
void QmitkRenderWindow::paintGL( )
{
  // Get the native window ID and pass it
  // to the m_Renderer
  // before we render for the first time...
  if(m_InitNeeded)
  {
    WId nId = winId();
    SetWindowId( (void*) nId );
    m_InitNeeded = false;
  }
  // rendering when not visible might cause serious problems (program may hang)
  if(QGLWidget::isVisible())
  {
    if(m_Renderer.IsNotNull())
      m_Renderer->Paint();
  }
}

void QmitkRenderWindow::showEvent ( QShowEvent * )
{
  // when the widget becomes visible the first time, we need to tell vtk the window size
  if(m_ResizeNeeded)
  {
    m_ResizeNeeded=false;
    if(m_Renderer.IsNotNull())
      m_Renderer->InitSize(width(),height());
  }
}

//##ModelId=3E3D1D4A00A5
void QmitkRenderWindow::MakeCurrent()
{
  makeCurrent();
}

void QmitkRenderWindow::SwapBuffers()
{
  swapBuffers();
};

bool QmitkRenderWindow::IsSharing () const
{
  return isSharing();
}

//##ModelId=3E6D5DD40306
void QmitkRenderWindow::mousePressEvent(QMouseEvent *me)
{
  QGLWidget::mousePressEvent(me);
  if (m_Renderer.IsNotNull())
  {
    mitk::Point2D p; p[0]=me->x(); p[1]=me->y();
    mitk::MouseEvent event(m_Renderer, me->type(), me->button(), me->state(), Qt::Key_unknown, p);
    m_Renderer->MousePressEvent(&event);
  }
}

//##ModelId=3E6D5DD4032E
void QmitkRenderWindow::mouseReleaseEvent(QMouseEvent *me)
{
  QGLWidget::mouseReleaseEvent(me);
  if (m_Renderer.IsNotNull())
  {
    mitk::Point2D p; p[0]=me->x(); p[1]=me->y();
    mitk::MouseEvent event(m_Renderer, me->type(), me->button(), me->state(), Qt::Key_unknown, p);
    m_Renderer->MouseReleaseEvent(&event);
  }
}

//##ModelId=3E6D5DD40356
void QmitkRenderWindow::mouseMoveEvent(QMouseEvent *me)
{
  QGLWidget::mouseMoveEvent(me);
  if (m_Renderer.IsNotNull()) {
    mitk::Point2D p; p[0]=me->x(); p[1]=me->y();
    mitk::MouseEvent event(m_Renderer, me->type(), me->button(), me->state(), Qt::Key_unknown, p);
    m_Renderer->MouseMoveEvent(&event);
  }
}

void QmitkRenderWindow::wheelEvent(QWheelEvent *we)
{
  we->ignore();
  /*  QGLWidget::wheelEvent(we);
  if (m_Renderer.IsNotNull())
    m_Renderer->WheelEvent(we); */
}

//##ModelId=3E6D5DD40388
void QmitkRenderWindow::keyPressEvent(QKeyEvent *ke)
{
  if (m_Renderer.IsNotNull())
  {
    QPoint cp = mapFromGlobal(QCursor::pos());
    mitk::KeyEvent mke(ke->type(), ke->key(), ke->ascii(), ke->state(), ke->text().ascii(), ke->isAutoRepeat(), ke->count(), cp.x(), cp.y(), QCursor::pos().x(), QCursor::pos().y());
    m_Renderer->KeyPressEvent(&mke);
    if(mke.isAccepted())
      ke->accept();
  }
}

//##ModelId=3EF59AD202B7
//check for update
void QmitkRenderWindow::Update()
{
  update();
};

//force an update
void QmitkRenderWindow::Repaint()
{
  //hide();//strongest update! but flickering widgets in zoom!
  //show();
  //updateGL();//not strong enough
  repaint();
};

QSize QmitkRenderWindow::minimumSizeHint () const
{
  return QSize(100, 100);
}

QSizePolicy QmitkRenderWindow::sizePolicy() const
{
  return QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding, 0, 0);
}

QSize QmitkRenderWindow::sizeHint () const
{
  return QSize(100, 100);
}

//##ModelId=3E6D5DD403B0
void QmitkRenderWindow::focusInEvent(QFocusEvent*)  {};
//##ModelId=3E6D5DD403E2
void QmitkRenderWindow::focusOutEvent(QFocusEvent*) {};

//We have to put this in a file containing a class that is directly used
//somewhere. Otherwise, e.g. when put in VtkRenderWindowInteractor.cpp,
//it is removed by the linker.
#include "VtkQRenderWindowInteractor.h"
VtkQRenderWindowInteractorFactory vtkQRenderWindowInteractorFactory;
<|endoftext|>


<commit_before>#include <string>
#include <stdint.h>

#include "buffer_t.h"
#include "JITCompiledModule.h"
#include "CodeGen.h"
#include "LLVM_Headers.h"
#include "Debug.h"

namespace Halide {
namespace Internal {

using std::string;

using namespace llvm;

// Wraps an execution engine. Takes ownership of the given module and
// the memory for jit compiled code.
class JITModuleHolder {
public:
    mutable RefCount ref_count;

    JITModuleHolder(llvm::ExecutionEngine *ee, llvm::Module *m, void (*stop_threads)()) :
        execution_engine(ee),
        module(m),
        context(&m->getContext()),
        shutdown_thread_pool(stop_threads) {
    }

    ~JITModuleHolder() {
        debug(2) << "Destroying JIT compiled module at " << this << "\n";
        for (size_t i = 0; i < cleanup_routines.size(); i++) {
            void *ptr = reinterpret_bits<void *>(cleanup_routines[i].fn);
            debug(2) << "  Calling cleanup routine [" << ptr << "]("
                     << cleanup_routines[i].context << ")\n";
            cleanup_routines[i].fn(cleanup_routines[i].context);
        }

        shutdown_thread_pool();
        execution_engine->runStaticConstructorsDestructors(true);
        delete execution_engine;
        delete context;
        // No need to delete the module - deleting the execution engine should take care of that.
    }

    ExecutionEngine *execution_engine;
    Module *module;
    LLVMContext *context;
    void (*shutdown_thread_pool)();

    /** Do any target-specific module cleanup. */
    std::vector<JITCompiledModule::CleanupRoutine> cleanup_routines;
};

template<>
EXPORT RefCount &ref_count<JITModuleHolder>(const JITModuleHolder *f) {return f->ref_count;}

template<>
EXPORT void destroy<JITModuleHolder>(const JITModuleHolder *f) {delete f;}

namespace {

#ifdef __arm__
// On ARM we need to track the addresses of all the functions we
// retrieve so that we can flush the icache.
char *start, *end;
#endif

// Retrieve a function pointer from an llvm module, possibly by
// compiling it. Returns it by assigning to the last argument.
template<typename FP>
void hook_up_function_pointer(ExecutionEngine *ee, Module *mod, const string &name, bool must_succeed, FP *result) {

    internal_assert(mod && ee);

    debug(2) << "Retrieving " << name << " from module\n";
    llvm::Function *fn = mod->getFunction(name);
    if (!fn) {
        if (must_succeed) {
            internal_error << "Could not find function " << name << " in module\n";
        } else {
            *result = NULL;
            return;
        }
    }

    debug(2) << "JIT Compiling " << name << "\n";
    void *f = ee->getPointerToFunction(fn);
    if (!f && must_succeed) {
        internal_error << "Compiling " << name << " returned NULL\n";
    }

    debug(2) << "Function " << name << " is at " << f << "\n";

    *result = reinterpret_bits<FP>(f);

    #ifdef __arm__
    if (start == NULL) {
        start = (char *)f;
        end = (char *)f;
    } else {
        start = std::min(start, (char *)f);
        end = std::max(end, (char *)f+32);
    }
    #endif
}

}

void JITCompiledModule::compile_module(CodeGen *cg, llvm::Module *m, const string &function_name) {

    // Make the execution engine
    debug(2) << "Creating new execution engine\n";
    string error_string;

    TargetOptions options;
    options.LessPreciseFPMADOption = true;
    options.NoFramePointerElim = false;
    options.AllowFPOpFusion = FPOpFusion::Fast;
    options.UnsafeFPMath = true;
    options.NoInfsFPMath = true;
    options.NoNaNsFPMath = true;
    options.HonorSignDependentRoundingFPMathOption = false;
    options.UseSoftFloat = false;
    options.FloatABIType =
        cg->use_soft_float_abi() ? FloatABI::Soft : FloatABI::Hard;
    options.NoZerosInBSS = false;
    options.GuaranteedTailCallOpt = false;
    options.DisableTailCalls = false;
    options.StackAlignmentOverride = 0;
    options.TrapFuncName = "";
    options.PositionIndependentExecutable = true;
    options.UseInitArray = false;

    #if LLVM_VERSION > 35
    EngineBuilder engine_builder((std::unique_ptr<llvm::Module>(m)));
    #else
    EngineBuilder engine_builder(m);
    #endif
    engine_builder.setTargetOptions(options);
    engine_builder.setErrorStr(&error_string);
    engine_builder.setEngineKind(EngineKind::JIT);
    #ifdef USE_MCJIT
    engine_builder.setUseMCJIT(true);
    JITMemoryManager *memory_manager = JITMemoryManager::CreateDefaultMemManager();
    engine_builder.setJITMemoryManager(memory_manager);
    #else
    engine_builder.setUseMCJIT(false);
    #endif
    engine_builder.setOptLevel(CodeGenOpt::Aggressive);
    engine_builder.setMCPU(cg->mcpu());
    engine_builder.setMAttrs(vec<string>(cg->mattrs()));
    ExecutionEngine *ee = engine_builder.create();
    if (!ee) std::cerr << error_string << "\n";
    internal_assert(ee) << "Couldn't create execution engine\n";

    #ifdef __arm__
    start = end = NULL;
    #endif

    // Do any target-specific initialization
    cg->jit_init(ee, m);

    // Retrieve function pointers from the compiled module (which also
    // triggers compilation)
    debug(1) << "JIT compiling...\n";

    hook_up_function_pointer(ee, m, function_name, true, &function);

    void *function_address = reinterpret_bits<void *>(function);
    debug(1) << "JIT compiled function pointer " << function_address << "\n";

    hook_up_function_pointer(ee, m, function_name + "_jit_wrapper", true, &wrapped_function);
    hook_up_function_pointer(ee, m, "halide_copy_to_host", false, &copy_to_host);
    hook_up_function_pointer(ee, m, "halide_copy_to_dev", false, &copy_to_dev);
    hook_up_function_pointer(ee, m, "halide_dev_free", false, &free_dev_buffer);
    hook_up_function_pointer(ee, m, "halide_set_error_handler", true, &set_error_handler);
    hook_up_function_pointer(ee, m, "halide_set_custom_allocator", true, &set_custom_allocator);
    hook_up_function_pointer(ee, m, "halide_set_custom_do_par_for", true, &set_custom_do_par_for);
    hook_up_function_pointer(ee, m, "halide_set_custom_do_task", true, &set_custom_do_task);
    hook_up_function_pointer(ee, m, "halide_set_custom_trace", true, &set_custom_trace);
    hook_up_function_pointer(ee, m, "halide_set_custom_print", true, &set_custom_print);
    hook_up_function_pointer(ee, m, "halide_shutdown_thread_pool", true, &shutdown_thread_pool);
    hook_up_function_pointer(ee, m, "halide_memoization_cache_set_size", true, &memoization_cache_set_size);

    debug(2) << "Finalizing object\n";
    ee->finalizeObject();

    // Stash the various objects that need to stay alive behind a reference-counted pointer.
    module = new JITModuleHolder(ee, m, shutdown_thread_pool);

    // Do any target-specific post-compilation module meddling
    cg->jit_finalize(ee, m, &module.ptr->cleanup_routines);

    #ifdef __arm__
    // Flush each function from the dcache so that it gets pulled into
    // the icache correctly.

    // finalizeMemory should have done the trick, but as of Aug 28
    // 2013, it doesn't work unless we also manually flush the
    // cache. Otherwise the icache's view of the code is missing the
    // relocations, which gets really confusing to debug, because
    // gdb's view of the code uses the dcache, so the disassembly
    // isn't right.
    debug(2) << "Flushing cache from " << (void *)start
             << " to " << (void *)end << "\n";
    __builtin___clear_cache(start, end);
    #endif

    // TODO: I don't think this is necessary, we shouldn't have any static constructors
    ee->runStaticConstructorsDestructors(false);

}

}
}
<commit_msg>Plausible-looking fix for trunk llvm.<commit_after>#include <string>
#include <stdint.h>

#include "buffer_t.h"
#include "JITCompiledModule.h"
#include "CodeGen.h"
#include "LLVM_Headers.h"
#include "Debug.h"

namespace Halide {
namespace Internal {

using std::string;

using namespace llvm;

// Wraps an execution engine. Takes ownership of the given module and
// the memory for jit compiled code.
class JITModuleHolder {
public:
    mutable RefCount ref_count;

    JITModuleHolder(llvm::ExecutionEngine *ee, llvm::Module *m, void (*stop_threads)()) :
        execution_engine(ee),
        module(m),
        context(&m->getContext()),
        shutdown_thread_pool(stop_threads) {
    }

    ~JITModuleHolder() {
        debug(2) << "Destroying JIT compiled module at " << this << "\n";
        for (size_t i = 0; i < cleanup_routines.size(); i++) {
            void *ptr = reinterpret_bits<void *>(cleanup_routines[i].fn);
            debug(2) << "  Calling cleanup routine [" << ptr << "]("
                     << cleanup_routines[i].context << ")\n";
            cleanup_routines[i].fn(cleanup_routines[i].context);
        }

        shutdown_thread_pool();
        execution_engine->runStaticConstructorsDestructors(true);
        delete execution_engine;
        delete context;
        // No need to delete the module - deleting the execution engine should take care of that.
    }

    ExecutionEngine *execution_engine;
    Module *module;
    LLVMContext *context;
    void (*shutdown_thread_pool)();

    /** Do any target-specific module cleanup. */
    std::vector<JITCompiledModule::CleanupRoutine> cleanup_routines;
};

template<>
EXPORT RefCount &ref_count<JITModuleHolder>(const JITModuleHolder *f) {return f->ref_count;}

template<>
EXPORT void destroy<JITModuleHolder>(const JITModuleHolder *f) {delete f;}

namespace {

#ifdef __arm__
// On ARM we need to track the addresses of all the functions we
// retrieve so that we can flush the icache.
char *start, *end;
#endif

// Retrieve a function pointer from an llvm module, possibly by
// compiling it. Returns it by assigning to the last argument.
template<typename FP>
void hook_up_function_pointer(ExecutionEngine *ee, Module *mod, const string &name, bool must_succeed, FP *result) {

    internal_assert(mod && ee);

    debug(2) << "Retrieving " << name << " from module\n";
    llvm::Function *fn = mod->getFunction(name);
    if (!fn) {
        if (must_succeed) {
            internal_error << "Could not find function " << name << " in module\n";
        } else {
            *result = NULL;
            return;
        }
    }

    debug(2) << "JIT Compiling " << name << "\n";
    void *f = ee->getPointerToFunction(fn);
    if (!f && must_succeed) {
        internal_error << "Compiling " << name << " returned NULL\n";
    }

    debug(2) << "Function " << name << " is at " << f << "\n";

    *result = reinterpret_bits<FP>(f);

    #ifdef __arm__
    if (start == NULL) {
        start = (char *)f;
        end = (char *)f;
    } else {
        start = std::min(start, (char *)f);
        end = std::max(end, (char *)f+32);
    }
    #endif
}

}

void JITCompiledModule::compile_module(CodeGen *cg, llvm::Module *m, const string &function_name) {

    // Make the execution engine
    debug(2) << "Creating new execution engine\n";
    string error_string;

    TargetOptions options;
    options.LessPreciseFPMADOption = true;
    options.NoFramePointerElim = false;
    options.AllowFPOpFusion = FPOpFusion::Fast;
    options.UnsafeFPMath = true;
    options.NoInfsFPMath = true;
    options.NoNaNsFPMath = true;
    options.HonorSignDependentRoundingFPMathOption = false;
    options.UseSoftFloat = false;
    options.FloatABIType =
        cg->use_soft_float_abi() ? FloatABI::Soft : FloatABI::Hard;
    options.NoZerosInBSS = false;
    options.GuaranteedTailCallOpt = false;
    options.DisableTailCalls = false;
    options.StackAlignmentOverride = 0;
    options.TrapFuncName = "";
    options.PositionIndependentExecutable = true;
    options.UseInitArray = false;

    #if LLVM_VERSION > 35
    EngineBuilder engine_builder((std::unique_ptr<llvm::Module>(m)));
    #else
    EngineBuilder engine_builder(m);
    #endif
    engine_builder.setTargetOptions(options);
    engine_builder.setErrorStr(&error_string);
    engine_builder.setEngineKind(EngineKind::JIT);
    #ifdef USE_MCJIT
    #if LLVM_VERSION < 36
    // >= 3.6 there is only mcjit
    engine_builder.setUseMCJIT(true);
    #endif
    JITMemoryManager *memory_manager = JITMemoryManager::CreateDefaultMemManager();
    engine_builder.setJITMemoryManager(memory_manager);
    #else
    engine_builder.setUseMCJIT(false);
    #endif
    engine_builder.setOptLevel(CodeGenOpt::Aggressive);
    engine_builder.setMCPU(cg->mcpu());
    engine_builder.setMAttrs(vec<string>(cg->mattrs()));
    ExecutionEngine *ee = engine_builder.create();
    if (!ee) std::cerr << error_string << "\n";
    internal_assert(ee) << "Couldn't create execution engine\n";

    #ifdef __arm__
    start = end = NULL;
    #endif

    // Do any target-specific initialization
    cg->jit_init(ee, m);

    // Retrieve function pointers from the compiled module (which also
    // triggers compilation)
    debug(1) << "JIT compiling...\n";

    hook_up_function_pointer(ee, m, function_name, true, &function);

    void *function_address = reinterpret_bits<void *>(function);
    debug(1) << "JIT compiled function pointer " << function_address << "\n";

    hook_up_function_pointer(ee, m, function_name + "_jit_wrapper", true, &wrapped_function);
    hook_up_function_pointer(ee, m, "halide_copy_to_host", false, &copy_to_host);
    hook_up_function_pointer(ee, m, "halide_copy_to_dev", false, &copy_to_dev);
    hook_up_function_pointer(ee, m, "halide_dev_free", false, &free_dev_buffer);
    hook_up_function_pointer(ee, m, "halide_set_error_handler", true, &set_error_handler);
    hook_up_function_pointer(ee, m, "halide_set_custom_allocator", true, &set_custom_allocator);
    hook_up_function_pointer(ee, m, "halide_set_custom_do_par_for", true, &set_custom_do_par_for);
    hook_up_function_pointer(ee, m, "halide_set_custom_do_task", true, &set_custom_do_task);
    hook_up_function_pointer(ee, m, "halide_set_custom_trace", true, &set_custom_trace);
    hook_up_function_pointer(ee, m, "halide_set_custom_print", true, &set_custom_print);
    hook_up_function_pointer(ee, m, "halide_shutdown_thread_pool", true, &shutdown_thread_pool);
    hook_up_function_pointer(ee, m, "halide_memoization_cache_set_size", true, &memoization_cache_set_size);

    debug(2) << "Finalizing object\n";
    ee->finalizeObject();

    // Stash the various objects that need to stay alive behind a reference-counted pointer.
    module = new JITModuleHolder(ee, m, shutdown_thread_pool);

    // Do any target-specific post-compilation module meddling
    cg->jit_finalize(ee, m, &module.ptr->cleanup_routines);

    #ifdef __arm__
    // Flush each function from the dcache so that it gets pulled into
    // the icache correctly.

    // finalizeMemory should have done the trick, but as of Aug 28
    // 2013, it doesn't work unless we also manually flush the
    // cache. Otherwise the icache's view of the code is missing the
    // relocations, which gets really confusing to debug, because
    // gdb's view of the code uses the dcache, so the disassembly
    // isn't right.
    debug(2) << "Flushing cache from " << (void *)start
             << " to " << (void *)end << "\n";
    __builtin___clear_cache(start, end);
    #endif

    // TODO: I don't think this is necessary, we shouldn't have any static constructors
    ee->runStaticConstructorsDestructors(false);

}

}
}
<|endoftext|>


<commit_before>#include <rang.hpp>
#include <cstdlib>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <functional>
#include <regex>
#include <date.h>

#include "Tools/Display/Terminal/Terminal.hpp"

#ifdef ENABLE_MPI
#include <mpi.h>
#endif

#include "Tools/general_utils.h"
#include "Tools/system_functions.h"
#include "Tools/Display/rang_format/rang_format.h"
#include "Tools/Exception/exception.hpp"

#include "Factory/Module/Source/Source.hpp"
#include "Factory/Module/CRC/CRC.hpp"
#include "Factory/Module/Encoder/Encoder.hpp"
#include "Factory/Module/Puncturer/Puncturer.hpp"
#include "Factory/Module/Interleaver/Interleaver.hpp"
#include "Factory/Module/Modem/Modem.hpp"
#include "Factory/Module/Channel/Channel.hpp"
#include "Factory/Module/Quantizer/Quantizer.hpp"
#include "Factory/Module/Decoder/Decoder.hpp"
#include "Factory/Module/Monitor/Monitor.hpp"
#include "Factory/Tools/Display/Terminal/Terminal.hpp"

#include "Launcher.hpp"

using namespace aff3ct;
using namespace aff3ct::launcher;

Launcher::Launcher(const int argc, const char **argv, factory::Simulation::parameters &params_common,
                   std::ostream &stream)
: simu(nullptr), ah(argc, argv), params_common(params_common), stream(stream)
{
	cmd_line += std::string(argv[0]) + std::string(" ");
	for (auto i = 1; i < argc; i++)
	{
		if (argv[i][0] == '-')
			cmd_line += std::string(argv[i]);
		else
			cmd_line += std::string("\"") + std::string(argv[i]) + std::string("\"");

		cmd_line += std::string(" ");
	}
}

Launcher::~Launcher()
{
}

void Launcher::get_description_args()
{
}

void Launcher::store_args()
{
}

int Launcher::read_arguments()
{
	this->get_description_args();

	std::vector<std::string> cmd_error;

	this->arg_vals = ah.parse_arguments(this->args, this->cmd_warn, cmd_error);

	try
	{
		this->store_args();
	}
	catch(const std::exception& e)
	{
		auto save = tools::exception::no_backtrace;
		tools::exception::no_backtrace = true;
		cmd_error.emplace_back(e.what());
		tools::exception::no_backtrace = save;
	}

#ifdef ENABLE_MPI
	if (this->params_common.mpi_rank == 0)
	{
#endif
		if (params_common.display_help)
		{
			auto grps = factory::Factory::create_groups({&params_common});
			ah.print_help(this->args, grps, params_common.display_adv_help);
		}

		// print usage
		if (!cmd_error.empty() && !params_common.display_help)
			ah.print_usage(this->args);

		// print the errors
		if (!cmd_error.empty()) std::cerr << std::endl;
		for (unsigned e = 0; e < cmd_error.size(); e++)
			std::cerr << rang::tag::error << cmd_error[e] << std::endl;

		// print the help tags
		if (!cmd_error.empty() && !params_common.display_help)
		{
			tools::Argument_tag help_tag = {"help", "h"};

			std::string message = "For more information please display the help (\"";
			message += tools::Argument_handler::print_tag(help_tag) += "\").";

			std::cerr << std::endl << rang::tag::info << message << std::endl;
		}
#ifdef ENABLE_MPI
	}
#endif

	return (!cmd_error.empty() || params_common.display_help) ? EXIT_FAILURE : EXIT_SUCCESS;
}

void Launcher::print_header()
{
	// display configuration and simulation parameters
	stream << rang::tag::comment << rang::style::bold << "----------------------------------------------------" << std::endl;
	stream << rang::tag::comment << rang::style::bold << "---- A FAST FORWARD ERROR CORRECTION TOOLBOX >> ----" << std::endl;
	stream << rang::tag::comment << rang::style::bold << "----------------------------------------------------" << std::endl;
	stream << rang::tag::comment << rang::style::bold << rang::style::underline << "Parameters :"<< rang::style::reset << std::endl;
	factory::Header::print_parameters({&params_common}, false, this->stream);
	this->stream << rang::tag::comment << std::endl;
}

std::string remove_sim_meta(const std::string& cmd)
{
#if !defined(__clang__) && !defined(__llvm__) && defined(__GNUC__) && defined(__cplusplus) && __GNUC__ < 5
	const std::string simmeta = " --sim-meta ";
	auto pos_arg = cmd.find(simmeta);

	if (pos_arg == std::string::npos)
		return cmd;

	auto pos_start = cmd.find("\"", pos_arg + simmeta.size());
	auto pos_end   = cmd.find("\"", pos_start + 1);

	return cmd.substr(0, pos_arg) + cmd.substr(pos_end + 1);
#else
	return std::regex_replace(cmd, std::regex("( --sim-meta \"[^\"]*\")"), "");
#endif
}

int Launcher::launch()
{
	int exit_code = EXIT_SUCCESS;

	std::srand((unsigned)this->params_common.global_seed);

	// in case of the user call launch multiple times
	if (simu != nullptr)
	{
		delete simu;
		simu = nullptr;
	}

	if (this->read_arguments() == EXIT_FAILURE)
	{
		// print the warnings
#ifdef ENABLE_MPI
		if (this->params_common.mpi_rank == 0)
#endif
			for (unsigned w = 0; w < this->cmd_warn.size(); w++)
				std::clog << rang::tag::warning << this->cmd_warn[w] << std::endl;
		return EXIT_FAILURE;
	}

	// write the command and he curve name in the PyBER format
#ifdef ENABLE_MPI
	if (this->params_common.mpi_rank == 0)
#endif
	if (!this->params_common.meta.empty())
	{
		stream << "[metadata]" << std::endl;
		stream << "command=" << remove_sim_meta(cmd_line) << std::endl;
		stream << "title=" << this->params_common.meta << std::endl;
		stream << std::endl << "[trace]" << std::endl;
	}

	if (this->params_common.display_legend)
#ifdef ENABLE_MPI
		if (this->params_common.mpi_rank == 0)
#endif
			this->print_header();

	// print the warnings
#ifdef ENABLE_MPI
	if (this->params_common.mpi_rank == 0)
#endif
		for (unsigned w = 0; w < this->cmd_warn.size(); w++)
			std::clog << rang::tag::warning << this->cmd_warn[w] << std::endl;

	try
	{
		simu = this->build_simu();
	}
	catch(const std::exception& e)
	{
		rang::format_on_each_line(std::cerr, std::string(e.what()) + "\n", rang::tag::error);
		exit_code = EXIT_FAILURE;
	}

	if (simu != nullptr)
	{
		// launch the simulation
		if (this->params_common.display_legend)
#ifdef ENABLE_MPI
			if (this->params_common.mpi_rank == 0)
#endif
				stream << rang::tag::comment << "The simulation is running..." << std::endl;

		try
		{
			simu->launch();
			if (simu->is_error())
				exit_code = EXIT_FAILURE;
		}
		catch(const std::exception& e)
		{
			rang::format_on_each_line(std::cerr, std::string(e.what()) + "\n", rang::tag::error);
			exit_code = EXIT_FAILURE;
		}
	}

	if (this->params_common.display_legend)
#ifdef ENABLE_MPI
		if (this->params_common.mpi_rank == 0)
#endif
			stream << rang::tag::comment << "End of the simulation." << std::endl;

	if (simu != nullptr)
	{
		delete simu;
		simu = nullptr;
	}

	return exit_code;
}
<commit_msg>Remove also argument -t in the metadata command line<commit_after>#include <rang.hpp>
#include <cstdlib>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <functional>
#include <regex>
#include <date.h>

#include "Tools/Display/Terminal/Terminal.hpp"

#ifdef ENABLE_MPI
#include <mpi.h>
#endif

#include "Tools/general_utils.h"
#include "Tools/system_functions.h"
#include "Tools/Display/rang_format/rang_format.h"
#include "Tools/Exception/exception.hpp"

#include "Factory/Module/Source/Source.hpp"
#include "Factory/Module/CRC/CRC.hpp"
#include "Factory/Module/Encoder/Encoder.hpp"
#include "Factory/Module/Puncturer/Puncturer.hpp"
#include "Factory/Module/Interleaver/Interleaver.hpp"
#include "Factory/Module/Modem/Modem.hpp"
#include "Factory/Module/Channel/Channel.hpp"
#include "Factory/Module/Quantizer/Quantizer.hpp"
#include "Factory/Module/Decoder/Decoder.hpp"
#include "Factory/Module/Monitor/Monitor.hpp"
#include "Factory/Tools/Display/Terminal/Terminal.hpp"

#include "Launcher.hpp"

using namespace aff3ct;
using namespace aff3ct::launcher;

Launcher::Launcher(const int argc, const char **argv, factory::Simulation::parameters &params_common,
                   std::ostream &stream)
: simu(nullptr), ah(argc, argv), params_common(params_common), stream(stream)
{
	cmd_line += std::string(argv[0]) + std::string(" ");
	for (auto i = 1; i < argc; i++)
	{
		if (argv[i][0] == '-')
			cmd_line += std::string(argv[i]);
		else
			cmd_line += std::string("\"") + std::string(argv[i]) + std::string("\"");

		cmd_line += std::string(" ");
	}
}

Launcher::~Launcher()
{
}

void Launcher::get_description_args()
{
}

void Launcher::store_args()
{
}

int Launcher::read_arguments()
{
	this->get_description_args();

	std::vector<std::string> cmd_error;

	this->arg_vals = ah.parse_arguments(this->args, this->cmd_warn, cmd_error);

	try
	{
		this->store_args();
	}
	catch(const std::exception& e)
	{
		auto save = tools::exception::no_backtrace;
		tools::exception::no_backtrace = true;
		cmd_error.emplace_back(e.what());
		tools::exception::no_backtrace = save;
	}

#ifdef ENABLE_MPI
	if (this->params_common.mpi_rank == 0)
	{
#endif
		if (params_common.display_help)
		{
			auto grps = factory::Factory::create_groups({&params_common});
			ah.print_help(this->args, grps, params_common.display_adv_help);
		}

		// print usage
		if (!cmd_error.empty() && !params_common.display_help)
			ah.print_usage(this->args);

		// print the errors
		if (!cmd_error.empty()) std::cerr << std::endl;
		for (unsigned e = 0; e < cmd_error.size(); e++)
			std::cerr << rang::tag::error << cmd_error[e] << std::endl;

		// print the help tags
		if (!cmd_error.empty() && !params_common.display_help)
		{
			tools::Argument_tag help_tag = {"help", "h"};

			std::string message = "For more information please display the help (\"";
			message += tools::Argument_handler::print_tag(help_tag) += "\").";

			std::cerr << std::endl << rang::tag::info << message << std::endl;
		}
#ifdef ENABLE_MPI
	}
#endif

	return (!cmd_error.empty() || params_common.display_help) ? EXIT_FAILURE : EXIT_SUCCESS;
}

void Launcher::print_header()
{
	// display configuration and simulation parameters
	stream << rang::tag::comment << rang::style::bold << "----------------------------------------------------" << std::endl;
	stream << rang::tag::comment << rang::style::bold << "---- A FAST FORWARD ERROR CORRECTION TOOLBOX >> ----" << std::endl;
	stream << rang::tag::comment << rang::style::bold << "----------------------------------------------------" << std::endl;
	stream << rang::tag::comment << rang::style::bold << rang::style::underline << "Parameters :"<< rang::style::reset << std::endl;
	factory::Header::print_parameters({&params_common}, false, this->stream);
	this->stream << rang::tag::comment << std::endl;
}

std::string remove_argument(const std::string& cmd, std::string arg)
{
#if !defined(__clang__) && !defined(__llvm__) && defined(__GNUC__) && defined(__cplusplus) && __GNUC__ < 5

	if (arg.front() != ' ')
		arg = " " + arg;
	auto pos_arg = cmd.find(arg);

	if (pos_arg == std::string::npos)
		return cmd;

	auto pos_start = cmd.find("\"", pos_arg + arg.size());
	auto pos_end   = cmd.find("\"", pos_start + 1);

	return cmd.substr(0, pos_arg) + cmd.substr(pos_end + 1);
#else
	return std::regex_replace(cmd, std::regex("( " + arg + " \"[^\"]*\")"), "");
#endif
}

int Launcher::launch()
{
	int exit_code = EXIT_SUCCESS;

	std::srand((unsigned)this->params_common.global_seed);

	// in case of the user call launch multiple times
	if (simu != nullptr)
	{
		delete simu;
		simu = nullptr;
	}

	if (this->read_arguments() == EXIT_FAILURE)
	{
		// print the warnings
#ifdef ENABLE_MPI
		if (this->params_common.mpi_rank == 0)
#endif
			for (unsigned w = 0; w < this->cmd_warn.size(); w++)
				std::clog << rang::tag::warning << this->cmd_warn[w] << std::endl;
		return EXIT_FAILURE;
	}

	// write the command and he curve name in the PyBER format
#ifdef ENABLE_MPI
	if (this->params_common.mpi_rank == 0)
#endif
	if (!this->params_common.meta.empty())
	{
		stream << "[metadata]" << std::endl;
		stream << "command=" << remove_argument(remove_argument(cmd_line, "--sim-meta"), "-t") << std::endl;
		stream << "title=" << this->params_common.meta << std::endl;
		stream << std::endl << "[trace]" << std::endl;
	}

	if (this->params_common.display_legend)
#ifdef ENABLE_MPI
		if (this->params_common.mpi_rank == 0)
#endif
			this->print_header();

	// print the warnings
#ifdef ENABLE_MPI
	if (this->params_common.mpi_rank == 0)
#endif
		for (unsigned w = 0; w < this->cmd_warn.size(); w++)
			std::clog << rang::tag::warning << this->cmd_warn[w] << std::endl;

	try
	{
		simu = this->build_simu();
	}
	catch(const std::exception& e)
	{
		rang::format_on_each_line(std::cerr, std::string(e.what()) + "\n", rang::tag::error);
		exit_code = EXIT_FAILURE;
	}

	if (simu != nullptr)
	{
		// launch the simulation
		if (this->params_common.display_legend)
#ifdef ENABLE_MPI
			if (this->params_common.mpi_rank == 0)
#endif
				stream << rang::tag::comment << "The simulation is running..." << std::endl;

		try
		{
			simu->launch();
			if (simu->is_error())
				exit_code = EXIT_FAILURE;
		}
		catch(const std::exception& e)
		{
			rang::format_on_each_line(std::cerr, std::string(e.what()) + "\n", rang::tag::error);
			exit_code = EXIT_FAILURE;
		}
	}

	if (this->params_common.display_legend)
#ifdef ENABLE_MPI
		if (this->params_common.mpi_rank == 0)
#endif
			stream << rang::tag::comment << "End of the simulation." << std::endl;

	if (simu != nullptr)
	{
		delete simu;
		simu = nullptr;
	}

	return exit_code;
}
<|endoftext|>