<commit_before>// Catch
#include <catch.hpp>

// C++ Standard Library
#include <memory>

// Mantella
#include <mantella>

class TestHillClimbing : public mant::HillClimbing {
  public:
    TestHillClimbing(
        const std::shared_ptr<mant::OptimisationProblem> optimisationProblem)
      : mant::HillClimbing(optimisationProblem),
        neighboursIndex_(0){

    }

    void setVelocitys(arma::mat neighbours){
      neighbours_ = neighbours;
    }

  protected:

    arma::Col<double> getRandomNeighbour(const arma::Col<double>& parameter,
                                         const double minimalDistance,
                                         const double maximalDistance) override {
      return neighbours_.col(neighboursIndex_++);
    }

    unsigned int neighboursIndex_;
    arma::mat neighbours_;
};

TEST_CASE("HillClimbing", "") {
  SECTION(".setMaximalStepSize") {
    std::shared_ptr<mant::OptimisationProblem> optimisationProblem(new mant::bbob::SphereFunction(2));
    mant::HillClimbing hillClimbing(optimisationProblem);

    SECTION("Test default value"){
      //TODO
    }

    SECTION("Test with parameter") {
      //TODO
    }
  }

  SECTION(".optimise") {
    // TODO
  }

  SECTION("Exception tests") {
    std::shared_ptr<mant::OptimisationProblem> optimisationProblem(new mant::bbob::SphereFunction(2));
    mant::HillClimbing hillClimbing(optimisationProblem);

    SECTION("Throws an exception, if the MaximalStepSize zero") {
//      CHECK_THROWS_AS(hillClimbing.setMaximalStepSize({0, 0}), std::logic_error);
    }

    SECTION("Throws an exception, if the size of MaximalStepSize is not equal to the number of dimension of the problem") {
//      CHECK_THROWS_AS(hillClimbing.setMaximalStepSize({100, 100, 100}), std::logic_error);
//      CHECK_THROWS_AS(hillClimbing.setMaximalStepSize({100}), std::logic_error);
    }

  }

  SECTION(".toString") {
    SECTION("Returns the expected class name.") {
      std::shared_ptr<mant::OptimisationProblem> optimisationProblem(new mant::bbob::SphereFunction(2));
      CHECK(mant::HillClimbing(optimisationProblem).toString() == "hill_climbing");
     }
  }
}
<commit_msg>Minor code style fixes<commit_after>// Catch
#include <catch.hpp>

// C++ Standard Library
#include <memory>

// Mantella
#include <mantella>

class TestHillClimbing : public mant::HillClimbing {
  public:
    TestHillClimbing(
        const std::shared_ptr<mant::OptimisationProblem> optimisationProblem)
      : mant::HillClimbing(optimisationProblem),
        neighboursIndex_(0){

    }

    void setVelocitys(
      const arma::Mat<double>& neighbours){
      neighbours_ = neighbours;
    }

  protected:

    arma::Col<double> getRandomNeighbour(const arma::Col<double>& parameter,
                                         const double minimalDistance,
                                         const double maximalDistance) override {
      return neighbours_.col(neighboursIndex_++);
    }

    unsigned int neighboursIndex_;
    arma::mat neighbours_;
};

TEST_CASE("HillClimbing", "") {
  SECTION(".setMaximalStepSize") {
    std::shared_ptr<mant::OptimisationProblem> optimisationProblem(new mant::bbob::SphereFunction(2));
    mant::HillClimbing hillClimbing(optimisationProblem);

    SECTION("Test default value"){
      //TODO
    }

    SECTION("Test with parameter") {
      //TODO
    }
  }

  SECTION(".optimise") {
    // TODO
  }

  SECTION("Exception tests") {
    std::shared_ptr<mant::OptimisationProblem> optimisationProblem(new mant::bbob::SphereFunction(2));
    mant::HillClimbing hillClimbing(optimisationProblem);

    SECTION("Throws an exception, if the MaximalStepSize zero") {
//      CHECK_THROWS_AS(hillClimbing.setMaximalStepSize({0, 0}), std::logic_error);
    }

    SECTION("Throws an exception, if the size of MaximalStepSize is not equal to the number of dimension of the problem") {
//      CHECK_THROWS_AS(hillClimbing.setMaximalStepSize({100, 100, 100}), std::logic_error);
//      CHECK_THROWS_AS(hillClimbing.setMaximalStepSize({100}), std::logic_error);
    }

  }

  SECTION(".toString") {
    SECTION("Returns the expected class name.") {
      std::shared_ptr<mant::OptimisationProblem> optimisationProblem(new mant::bbob::SphereFunction(2));
      CHECK(mant::HillClimbing(optimisationProblem).toString() == "hill_climbing");
     }
  }
}
<|endoftext|>


<commit_before>#ifdef ENABLE_MPI

#include "CellCommunicator.h"

/*!
 * \param sysdef System definition
 * \param cl MPCD cell list
 */
mpcd::CellCommunicator::CellCommunicator(std::shared_ptr<SystemDefinition> sysdef,
                                         std::shared_ptr<mpcd::CellList> cl)
    : m_sysdef(sysdef),
      m_pdata(sysdef->getParticleData()),
      m_exec_conf(m_pdata->getExecConf()),
      m_mpi_comm(m_exec_conf->getMPICommunicator()),
      m_decomposition(m_pdata->getDomainDecomposition()),
      m_cl(cl),
      m_communicating(false),
      m_send_buf(m_exec_conf),
      m_recv_buf(m_exec_conf),
      m_needs_init(true)
    {
    m_exec_conf->msg->notice(5) << "Constructing MPCD CellCommunicator" << std::endl;
    #ifdef ENABLE_CUDA
    if (m_exec_conf->isCUDAEnabled())
        {
        m_tuner_pack.reset(new Autotuner(32, 1024, 32, 5, 100000, "mpcd_cell_comm_pack", m_exec_conf));
        m_tuner_unpack.reset(new Autotuner(32, 1024, 32, 5, 100000, "mpcd_cell_comm_unpack", m_exec_conf));
        }
    #endif // ENABLE_CUDA

    m_cl->getSizeChangeSignal().connect<mpcd::CellCommunicator, &mpcd::CellCommunicator::slotInit>(this);
    }

mpcd::CellCommunicator::~CellCommunicator()
    {
    m_exec_conf->msg->notice(5) << "Destroying MPCD CellCommunicator" << std::endl;
    m_cl->getSizeChangeSignal().disconnect<mpcd::CellCommunicator, &mpcd::CellCommunicator::slotInit>(this);
    }

namespace mpcd
{
namespace detail
{
//! Unary operator to wrap global cell indexes into the local domain
struct LocalCellWrapOp
    {
    LocalCellWrapOp(std::shared_ptr<mpcd::CellList> cl_)
        : cl(cl_), ci(cl_->getCellIndexer()), gci(cl_->getGlobalCellIndexer())
        { }

    //! Transform the global 1D cell index into a local 1D cell index
    inline unsigned int operator()(unsigned int cell_idx)
        {
        // convert the 1D global cell index to a global cell tuple
        const uint3 cell = gci.getTriple(cell_idx);

        // convert the global cell tuple to a local cell tuple
        int3 local_cell = cl->getLocalCell(make_int3(cell.x, cell.y, cell.z));

        // wrap the local cell through the global boundaries, which should work for all reasonable cell comms.
        if (local_cell.x >= (int)gci.getW()) local_cell.x -= gci.getW();
        else if (local_cell.x < 0) local_cell.x += gci.getW();

        if (local_cell.y >= (int)gci.getH()) local_cell.y -= gci.getH();
        else if (local_cell.y < 0) local_cell.y += gci.getH();

        if (local_cell.z >= (int)gci.getD()) local_cell.z -= gci.getD();
        else if (local_cell.z < 0) local_cell.z += gci.getD();

        // convert the local cell tuple back to an index
        return ci(local_cell.x, local_cell.y, local_cell.z);
        }

    std::shared_ptr<mpcd::CellList> cl; //!< Cell list
    const Index3D ci;                   //!< Cell indexer
    const Index3D gci;                  //!< Global cell indexer
    };
} // end namespace detail
} // end namespace mpcd

void mpcd::CellCommunicator::initialize()
    {
    // obtain domain decomposition
    const Index3D& di = m_decomposition->getDomainIndexer();
    ArrayHandle<unsigned int> h_cart_ranks(m_decomposition->getCartRanks(), access_location::host, access_mode::read);
    const uint3 my_pos = m_decomposition->getGridPos();

    // use the cell list to compute the bounds
    const Index3D& ci = m_cl->getCellIndexer();
    const Index3D& global_ci = m_cl->getGlobalCellIndexer();
    auto num_comm_cells = m_cl->getNComm();
    const uint3 max_lo = make_uint3(num_comm_cells[static_cast<unsigned int>(mpcd::detail::face::west)],
                                    num_comm_cells[static_cast<unsigned int>(mpcd::detail::face::south)],
                                    num_comm_cells[static_cast<unsigned int>(mpcd::detail::face::down)]);
    const uint3 min_hi = make_uint3(ci.getW() - num_comm_cells[static_cast<unsigned int>(mpcd::detail::face::east)],
                                    ci.getH() - num_comm_cells[static_cast<unsigned int>(mpcd::detail::face::north)],
                                    ci.getD() - num_comm_cells[static_cast<unsigned int>(mpcd::detail::face::up)]);

    // check to make sure box is not overdecomposed
        {
        const unsigned int nextra = m_cl->getNExtraCells();
        unsigned int err = ((max_lo.x + nextra) > min_hi.x ||
                            (max_lo.y + nextra) > min_hi.y ||
                            (max_lo.z + nextra) > min_hi.z);
        MPI_Allreduce(MPI_IN_PLACE, &err, 1, MPI_UNSIGNED, MPI_MAX, m_mpi_comm);
        if (err)
            {
            m_exec_conf->msg->error() << "mpcd: Simulation box is overdecomposed, decrease the number of ranks." << std::endl;
            throw std::runtime_error("Simulation box is overdecomposed for MPCD");
            }
        }

    // loop over all cells in the grid and determine where to send them
    std::multimap<unsigned int, unsigned int> send_map;
    std::set<unsigned int> neighbors;
    for (unsigned int k=0; k < ci.getD(); ++k)
        {
        for (unsigned int j=0; j < ci.getH(); ++j)
            {
            for (unsigned int i=0; i < ci.getW(); ++i)
                {
                // skip any cells interior to the grid, which will not be communicated
                // this is wasteful loop logic, but initialize will only be called rarely
                if (i >= max_lo.x && i < min_hi.x &&
                    j >= max_lo.y && j < min_hi.y &&
                    k >= max_lo.z && k < min_hi.z)
                    continue;

                // obtain the 1D global index of this cell
                const int3 global_cell = m_cl->getGlobalCell(make_int3(i,j,k));
                const unsigned int global_cell_idx = global_ci(global_cell.x, global_cell.y, global_cell.z);

                // check which direction the cell lies off rank in x,y,z
                std::vector<int> dx = {0};
                if (i < max_lo.x)
                    dx.push_back(-1);
                else if (i >= min_hi.x)
                    dx.push_back(1);

                std::vector<int> dy = {0};
                if (j < max_lo.y)
                    dy.push_back(-1);
                else if (j >= min_hi.y)
                    dy.push_back(1);

                std::vector<int> dz = {0};
                if (k < max_lo.z)
                    dz.push_back(-1);
                else if (k >= min_hi.z)
                    dz.push_back(1);

                // generate all permutations of these neighbors for the cell
                for (auto ddx = dx.begin(); ddx != dx.end(); ++ddx)
                    {
                    for (auto ddy = dy.begin(); ddy != dy.end(); ++ddy)
                        {
                        for (auto ddz = dz.begin(); ddz != dz.end(); ++ddz)
                            {
                            // skip self
                            if (*ddx == 0 && *ddy == 0 && *ddz == 0) continue;

                            // get neighbor rank tuple
                            int3 neigh = make_int3((int)my_pos.x + *ddx,
                                                   (int)my_pos.y + *ddy,
                                                   (int)my_pos.z + *ddz);

                            // wrap neighbor through the boundaries
                            if (neigh.x < 0)
                                neigh.x += di.getW();
                            else if (neigh.x >= (int)di.getW())
                                neigh.x -= di.getW();

                            if (neigh.y < 0)
                                neigh.y += di.getH();
                            else if (neigh.y >= (int)di.getH())
                                neigh.y -= di.getH();

                            if (neigh.z < 0)
                                neigh.z += di.getD();
                            else if (neigh.z >= (int)di.getD())
                                neigh.z -= di.getD();

                            // convert neighbor to a linear rank and push it into the unique neighbor set
                            const unsigned int neigh_rank = h_cart_ranks.data[di(neigh.x,neigh.y,neigh.z)];
                            neighbors.insert(neigh_rank);
                            send_map.insert(std::make_pair(neigh_rank, global_cell_idx));
                            } // ddz
                        } // ddy
                    } // ddx
                } // i
            } // j
        } // k

    // allocate send / receive index arrays
        {
        GPUArray<unsigned int> send_idx(send_map.size(), m_exec_conf);
        m_send_idx.swap(send_idx);
        }

    // fill the send indexes with the global values
    // flood the array of unique neighbors and count the number to send
        {
        ArrayHandle<unsigned int> h_send_idx(m_send_idx, access_location::host, access_mode::overwrite);
        unsigned int idx = 0;
        for (auto it = send_map.begin(); it != send_map.end(); ++it)
            {
            h_send_idx.data[idx++] = it->second;
            }

        m_neighbors.resize(neighbors.size());
        m_begin.resize(m_neighbors.size());
        m_num_send.resize(m_neighbors.size());
        idx = 0;
        for (auto it = neighbors.begin(); it != neighbors.end(); ++it)
            {
            auto lower = send_map.lower_bound(*it);
            auto upper = send_map.upper_bound(*it);

            m_neighbors[idx] = *it;
            m_begin[idx] = std::distance(send_map.begin(), lower);
            m_num_send[idx] = std::distance(lower, upper);
            ++idx;
            }
        }

    // send / receive the global cell indexes to be communicated with neighbors
    std::vector<unsigned int> recv_idx(m_send_idx.getNumElements());
        {
        ArrayHandle<unsigned int> h_send_idx(m_send_idx, access_location::host, access_mode::read);

        m_reqs.resize(2*m_neighbors.size());
        for (unsigned int idx=0; idx < m_neighbors.size(); ++idx)
            {
            const unsigned int offset = m_begin[idx];
            MPI_Isend(h_send_idx.data + offset, m_num_send[idx], MPI_INT, m_neighbors[idx], 0, m_mpi_comm, &m_reqs[2*idx]);
            MPI_Irecv(recv_idx.data() + offset, m_num_send[idx], MPI_INT, m_neighbors[idx], 0, m_mpi_comm, &m_reqs[2*idx+1]);
            }
        MPI_Waitall(m_reqs.size(), m_reqs.data(), MPI_STATUSES_IGNORE);
        }

    // transform all of the global cell indexes back into local cell indexes
        {
        ArrayHandle<unsigned int> h_send_idx(m_send_idx, access_location::host, access_mode::readwrite);

        mpcd::detail::LocalCellWrapOp wrapper(m_cl);
        std::transform(h_send_idx.data, h_send_idx.data + m_send_idx.getNumElements(), h_send_idx.data, wrapper);
        std::transform(recv_idx.begin(), recv_idx.end(), recv_idx.begin(), wrapper);
        }

    // map the received cells from a rank-basis to a cell-basis
        {
        std::multimap<unsigned int, unsigned int> cell_map;
        std::set<unsigned int> unique_cells;
        for (unsigned int idx=0; idx < recv_idx.size(); ++idx)
            {
            const unsigned int cell = recv_idx[idx];
            unique_cells.insert(cell);
            cell_map.insert(std::make_pair(cell, idx));
            }
        m_num_cells = unique_cells.size();

        /*
         * Allocate auxiliary memory for receiving cell reordering
         */
            {
            GPUArray<unsigned int> recv(recv_idx.size(), m_exec_conf);
            m_recv.swap(recv);

            GPUArray<unsigned int> cells(m_num_cells, m_exec_conf);
            m_cells.swap(cells);

            GPUArray<unsigned int> recv_begin(m_num_cells, m_exec_conf);
            m_recv_begin.swap(recv_begin);

            GPUArray<unsigned int> recv_end(m_num_cells, m_exec_conf);
            m_recv_end.swap(recv_end);
            }

        /*
         * Write out the resorted cells from the map, and determine the range of data belonging to each received cell
         */
        ArrayHandle<unsigned int> h_recv(m_recv, access_location::host, access_mode::overwrite);
        unsigned int idx = 0;
        for (auto it = cell_map.begin(); it != cell_map.end(); ++it)
            {
            h_recv.data[idx++] = it->second;
            }

        ArrayHandle<unsigned int> h_cells(m_cells, access_location::host, access_mode::overwrite);
        ArrayHandle<unsigned int> h_recv_begin(m_recv_begin, access_location::host, access_mode::overwrite);
        ArrayHandle<unsigned int> h_recv_end(m_recv_end, access_location::host, access_mode::overwrite);
        idx = 0;
        for (auto it = unique_cells.begin(); it != unique_cells.end(); ++it)
            {
            auto lower = cell_map.lower_bound(*it);
            auto upper = cell_map.upper_bound(*it);

            h_cells.data[idx] = *it;
            h_recv_begin.data[idx] = std::distance(cell_map.begin(), lower);
            h_recv_end.data[idx] = std::distance(cell_map.begin(), upper);

            ++idx;
            }
        }
    }

#endif // ENABLE_MPI
<commit_msg>Use a linear search on the map for performance<commit_after>#ifdef ENABLE_MPI

#include "CellCommunicator.h"

/*!
 * \param sysdef System definition
 * \param cl MPCD cell list
 */
mpcd::CellCommunicator::CellCommunicator(std::shared_ptr<SystemDefinition> sysdef,
                                         std::shared_ptr<mpcd::CellList> cl)
    : m_sysdef(sysdef),
      m_pdata(sysdef->getParticleData()),
      m_exec_conf(m_pdata->getExecConf()),
      m_mpi_comm(m_exec_conf->getMPICommunicator()),
      m_decomposition(m_pdata->getDomainDecomposition()),
      m_cl(cl),
      m_communicating(false),
      m_send_buf(m_exec_conf),
      m_recv_buf(m_exec_conf),
      m_needs_init(true)
    {
    m_exec_conf->msg->notice(5) << "Constructing MPCD CellCommunicator" << std::endl;
    #ifdef ENABLE_CUDA
    if (m_exec_conf->isCUDAEnabled())
        {
        m_tuner_pack.reset(new Autotuner(32, 1024, 32, 5, 100000, "mpcd_cell_comm_pack", m_exec_conf));
        m_tuner_unpack.reset(new Autotuner(32, 1024, 32, 5, 100000, "mpcd_cell_comm_unpack", m_exec_conf));
        }
    #endif // ENABLE_CUDA

    m_cl->getSizeChangeSignal().connect<mpcd::CellCommunicator, &mpcd::CellCommunicator::slotInit>(this);
    }

mpcd::CellCommunicator::~CellCommunicator()
    {
    m_exec_conf->msg->notice(5) << "Destroying MPCD CellCommunicator" << std::endl;
    m_cl->getSizeChangeSignal().disconnect<mpcd::CellCommunicator, &mpcd::CellCommunicator::slotInit>(this);
    }

namespace mpcd
{
namespace detail
{
//! Unary operator to wrap global cell indexes into the local domain
struct LocalCellWrapOp
    {
    LocalCellWrapOp(std::shared_ptr<mpcd::CellList> cl_)
        : cl(cl_), ci(cl_->getCellIndexer()), gci(cl_->getGlobalCellIndexer())
        { }

    //! Transform the global 1D cell index into a local 1D cell index
    inline unsigned int operator()(unsigned int cell_idx)
        {
        // convert the 1D global cell index to a global cell tuple
        const uint3 cell = gci.getTriple(cell_idx);

        // convert the global cell tuple to a local cell tuple
        int3 local_cell = cl->getLocalCell(make_int3(cell.x, cell.y, cell.z));

        // wrap the local cell through the global boundaries, which should work for all reasonable cell comms.
        if (local_cell.x >= (int)gci.getW()) local_cell.x -= gci.getW();
        else if (local_cell.x < 0) local_cell.x += gci.getW();

        if (local_cell.y >= (int)gci.getH()) local_cell.y -= gci.getH();
        else if (local_cell.y < 0) local_cell.y += gci.getH();

        if (local_cell.z >= (int)gci.getD()) local_cell.z -= gci.getD();
        else if (local_cell.z < 0) local_cell.z += gci.getD();

        // convert the local cell tuple back to an index
        return ci(local_cell.x, local_cell.y, local_cell.z);
        }

    std::shared_ptr<mpcd::CellList> cl; //!< Cell list
    const Index3D ci;                   //!< Cell indexer
    const Index3D gci;                  //!< Global cell indexer
    };
} // end namespace detail
} // end namespace mpcd

void mpcd::CellCommunicator::initialize()
    {
    // obtain domain decomposition
    const Index3D& di = m_decomposition->getDomainIndexer();
    ArrayHandle<unsigned int> h_cart_ranks(m_decomposition->getCartRanks(), access_location::host, access_mode::read);
    const uint3 my_pos = m_decomposition->getGridPos();

    // use the cell list to compute the bounds
    const Index3D& ci = m_cl->getCellIndexer();
    const Index3D& global_ci = m_cl->getGlobalCellIndexer();
    auto num_comm_cells = m_cl->getNComm();
    const uint3 max_lo = make_uint3(num_comm_cells[static_cast<unsigned int>(mpcd::detail::face::west)],
                                    num_comm_cells[static_cast<unsigned int>(mpcd::detail::face::south)],
                                    num_comm_cells[static_cast<unsigned int>(mpcd::detail::face::down)]);
    const uint3 min_hi = make_uint3(ci.getW() - num_comm_cells[static_cast<unsigned int>(mpcd::detail::face::east)],
                                    ci.getH() - num_comm_cells[static_cast<unsigned int>(mpcd::detail::face::north)],
                                    ci.getD() - num_comm_cells[static_cast<unsigned int>(mpcd::detail::face::up)]);

    // check to make sure box is not overdecomposed
        {
        const unsigned int nextra = m_cl->getNExtraCells();
        unsigned int err = ((max_lo.x + nextra) > min_hi.x ||
                            (max_lo.y + nextra) > min_hi.y ||
                            (max_lo.z + nextra) > min_hi.z);
        MPI_Allreduce(MPI_IN_PLACE, &err, 1, MPI_UNSIGNED, MPI_MAX, m_mpi_comm);
        if (err)
            {
            m_exec_conf->msg->error() << "mpcd: Simulation box is overdecomposed, decrease the number of ranks." << std::endl;
            throw std::runtime_error("Simulation box is overdecomposed for MPCD");
            }
        }

    // loop over all cells in the grid and determine where to send them
    std::multimap<unsigned int, unsigned int> send_map;
    std::set<unsigned int> neighbors;
    for (unsigned int k=0; k < ci.getD(); ++k)
        {
        for (unsigned int j=0; j < ci.getH(); ++j)
            {
            for (unsigned int i=0; i < ci.getW(); ++i)
                {
                // skip any cells interior to the grid, which will not be communicated
                // this is wasteful loop logic, but initialize will only be called rarely
                if (i >= max_lo.x && i < min_hi.x &&
                    j >= max_lo.y && j < min_hi.y &&
                    k >= max_lo.z && k < min_hi.z)
                    continue;

                // obtain the 1D global index of this cell
                const int3 global_cell = m_cl->getGlobalCell(make_int3(i,j,k));
                const unsigned int global_cell_idx = global_ci(global_cell.x, global_cell.y, global_cell.z);

                // check which direction the cell lies off rank in x,y,z
                std::vector<int> dx = {0};
                if (i < max_lo.x)
                    dx.push_back(-1);
                else if (i >= min_hi.x)
                    dx.push_back(1);

                std::vector<int> dy = {0};
                if (j < max_lo.y)
                    dy.push_back(-1);
                else if (j >= min_hi.y)
                    dy.push_back(1);

                std::vector<int> dz = {0};
                if (k < max_lo.z)
                    dz.push_back(-1);
                else if (k >= min_hi.z)
                    dz.push_back(1);

                // generate all permutations of these neighbors for the cell
                for (auto ddx = dx.begin(); ddx != dx.end(); ++ddx)
                    {
                    for (auto ddy = dy.begin(); ddy != dy.end(); ++ddy)
                        {
                        for (auto ddz = dz.begin(); ddz != dz.end(); ++ddz)
                            {
                            // skip self
                            if (*ddx == 0 && *ddy == 0 && *ddz == 0) continue;

                            // get neighbor rank tuple
                            int3 neigh = make_int3((int)my_pos.x + *ddx,
                                                   (int)my_pos.y + *ddy,
                                                   (int)my_pos.z + *ddz);

                            // wrap neighbor through the boundaries
                            if (neigh.x < 0)
                                neigh.x += di.getW();
                            else if (neigh.x >= (int)di.getW())
                                neigh.x -= di.getW();

                            if (neigh.y < 0)
                                neigh.y += di.getH();
                            else if (neigh.y >= (int)di.getH())
                                neigh.y -= di.getH();

                            if (neigh.z < 0)
                                neigh.z += di.getD();
                            else if (neigh.z >= (int)di.getD())
                                neigh.z -= di.getD();

                            // convert neighbor to a linear rank and push it into the unique neighbor set
                            const unsigned int neigh_rank = h_cart_ranks.data[di(neigh.x,neigh.y,neigh.z)];
                            neighbors.insert(neigh_rank);
                            send_map.insert(std::make_pair(neigh_rank, global_cell_idx));
                            } // ddz
                        } // ddy
                    } // ddx
                } // i
            } // j
        } // k

    // allocate send / receive index arrays
        {
        GPUArray<unsigned int> send_idx(send_map.size(), m_exec_conf);
        m_send_idx.swap(send_idx);
        }

    // fill the send indexes with the global values
    // flood the array of unique neighbors and count the number to send
        {
        ArrayHandle<unsigned int> h_send_idx(m_send_idx, access_location::host, access_mode::overwrite);
        unsigned int idx = 0;
        for (auto it = send_map.begin(); it != send_map.end(); ++it)
            {
            h_send_idx.data[idx++] = it->second;
            }

        m_neighbors.resize(neighbors.size());
        m_begin.resize(m_neighbors.size());
        m_num_send.resize(m_neighbors.size());
        idx = 0;
        for (auto it = neighbors.begin(); it != neighbors.end(); ++it)
            {
            auto lower = send_map.lower_bound(*it);
            auto upper = send_map.upper_bound(*it);

            m_neighbors[idx] = *it;
            m_begin[idx] = std::distance(send_map.begin(), lower);
            m_num_send[idx] = std::distance(lower, upper);
            ++idx;
            }
        }

    // send / receive the global cell indexes to be communicated with neighbors
    std::vector<unsigned int> recv_idx(m_send_idx.getNumElements());
        {
        ArrayHandle<unsigned int> h_send_idx(m_send_idx, access_location::host, access_mode::read);

        m_reqs.resize(2*m_neighbors.size());
        for (unsigned int idx=0; idx < m_neighbors.size(); ++idx)
            {
            const unsigned int offset = m_begin[idx];
            MPI_Isend(h_send_idx.data + offset, m_num_send[idx], MPI_INT, m_neighbors[idx], 0, m_mpi_comm, &m_reqs[2*idx]);
            MPI_Irecv(recv_idx.data() + offset, m_num_send[idx], MPI_INT, m_neighbors[idx], 0, m_mpi_comm, &m_reqs[2*idx+1]);
            }
        MPI_Waitall(m_reqs.size(), m_reqs.data(), MPI_STATUSES_IGNORE);
        }

    // transform all of the global cell indexes back into local cell indexes
        {
        ArrayHandle<unsigned int> h_send_idx(m_send_idx, access_location::host, access_mode::readwrite);

        mpcd::detail::LocalCellWrapOp wrapper(m_cl);
        std::transform(h_send_idx.data, h_send_idx.data + m_send_idx.getNumElements(), h_send_idx.data, wrapper);
        std::transform(recv_idx.begin(), recv_idx.end(), recv_idx.begin(), wrapper);
        }

    // map the received cells from a rank-basis to a cell-basis
        {
        std::multimap<unsigned int, unsigned int> cell_map;
        std::set<unsigned int> unique_cells;
        for (unsigned int idx=0; idx < recv_idx.size(); ++idx)
            {
            const unsigned int cell = recv_idx[idx];
            unique_cells.insert(cell);
            cell_map.insert(std::make_pair(cell, idx));
            }
        m_num_cells = unique_cells.size();

        /*
         * Allocate auxiliary memory for receiving cell reordering
         */
            {
            GPUArray<unsigned int> recv(recv_idx.size(), m_exec_conf);
            m_recv.swap(recv);

            GPUArray<unsigned int> cells(m_num_cells, m_exec_conf);
            m_cells.swap(cells);

            GPUArray<unsigned int> recv_begin(m_num_cells, m_exec_conf);
            m_recv_begin.swap(recv_begin);

            GPUArray<unsigned int> recv_end(m_num_cells, m_exec_conf);
            m_recv_end.swap(recv_end);
            }

        /*
         * Generate the compacted list of unique cells
         */
        ArrayHandle<unsigned int> h_cells(m_cells, access_location::host, access_mode::overwrite);
        idx = 0;
        for (auto it = unique_cells.begin(); it != unique_cells.end(); ++it)
            {
            h_cells.data[idx++] = *it;
            }

        /*
         * Loop over the cell map to do run-length encoding on the keys. This
         * determines the range of data belonging to each received cell.
         */
        ArrayHandle<unsigned int> h_recv_begin(m_recv_begin, access_location::host, access_mode::overwrite);
        ArrayHandle<unsigned int> h_recv_end(m_recv_end, access_location::host, access_mode::overwrite);
        unsigned int last_cell = UINT_MAX;
        unsigned int cell_idx = 0;
        idx = 0;
        h_recv_begin.data[cell_idx] = idx;
        for (auto it = cell_map.begin(); it != cell_map.end(); ++it)
            {
            // record the sorted receive index
            h_recv.data[idx] = it->second;

            // if not very first pass and the current cell does not match the
            // last cell, then we are on a new cell, and need to demark an end / begin
            if (last_cell != UINT_MAX && it->first != last_cell)
                {
                h_recv_end.data[cell_idx] = idx;
                h_recv_begin.data[++cell_idx] = idx;
                }
            last_cell = it->first;

            ++idx;
            }
        h_recv_end.data[cell_idx] = idx;
        }
    }

#endif // ENABLE_MPI
<|endoftext|>


<commit_before>//
// @author @Yue Wang @shbling @Soyn @Yue Wang
//
// Exercise 10.24:
// Use bind and check_size to find the first element in a vector of ints that has a value greater
// than the length of a specified string value.
//
//  Discussion over this exercise on StackOverflow
//  http://stackoverflow.com/questions/20539406/what-type-does-stdfind-if-not-return
//

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <functional>

using std::cout;
using std::endl;
using std::string;
using std::vector;
using std::find_if;
using std::bind;

inline auto check_size(string const& s, string::size_type sz) -> bool
{
    return s.size() < sz;
}

inline auto find_first_greater(vector<int> const& v, string const& s) -> vector<int>::const_iterator
{
    auto lambda = [&](int i){ return i >= 0 && bind(check_size, s, i)(); };
    return find_if(v.cbegin(), v.cend(), lambda);
}

int main()
{
    vector<int> v{ -1, -2, 3, 4, 5, 6, 7 };
    string s("test");
    cout << *find_first_greater(v, s) << endl;

    return 0;
}
<commit_msg>fix : #345 with C++14 syntax<commit_after>//
// @author @Yue Wang @shbling @Soyn @Yue Wang
//
// Exercise 10.24:
// Use bind and check_size to find the first element in a vector of ints that has a value greater
// than the length of a specified string value.
//
//  Discussion over this exercise on StackOverflow
//  http://stackoverflow.com/questions/20539406/what-type-does-stdfind-if-not-return
//

#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>

using std::cout;
using std::endl;
using std::string;
using std::vector;
using std::find_if;
using std::bind;
using std::size_t;

auto check_size(string const& str, size_t sz)
{
    return str.size() < sz;
}

auto find_first_greater(vector<int> const& v, string const& str)
{
    auto predicate = [&](int i){ return bind(check_size, str, i)(); };
    return find_if(v.cbegin(), v.cend(), predicate);
}

int main()
{
    vector<int> vec{ 0, 1, 2, 3, 4, 5, 6, 7 };
    string str("123456");
    auto result = find_first_greater(vec, str);
    if (result != vec.cend())
        cout << *result << endl;
    else
        cout << "Not found" << endl;

    return 0;
}
<|endoftext|>


<commit_before>#include "dcdsupport.h"

#include "dlangdebughelper.h"
#include <dlangoptionspage.h>

#include <stdexcept>

#include <QProcess>
#include <QTextStream>
#include <QDebug>

#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/project.h>
#include <cpptools/cppmodelmanager.h>

using namespace Dcd;

DcdClient::DcdClient(const QString &projectName, const QString &processName, int port, QObject *parent)
    : QObject(parent), m_projectName(projectName), m_port(port), m_processName(processName)
{
    m_portArguments << QLatin1String("--port") << QString::number(port);
}

const QString &DcdClient::projectName() const
{
    return m_projectName;
}

void DcdClient::setOutputFile(const QString &filePath)
{
    m_filePath = filePath;
}

void startProcess(QProcess &p, const QString &processName, const QStringList &args,
                  const QString &filePath, QIODevice::OpenMode mode = QIODevice::ReadWrite)
{
    if (p.state() != QProcess::NotRunning) {
        throw std::runtime_error("process is already running");
    }
    if (!filePath.isEmpty()) {
        p.setStandardOutputFile(filePath, QIODevice::Append | QIODevice::Unbuffered);
        p.setStandardErrorFile(filePath, QIODevice::Append | QIODevice::Unbuffered);
    }
    p.start(processName, args, mode);
    if (!p.waitForStarted(1000)) {
        throw std::runtime_error("process start timeout");
    }
}

void waitForFinished(QProcess &p)
{
    if (!p.waitForFinished(1000)) {
        throw std::runtime_error("process finish timeout");
    }
    if (p.exitStatus() != QProcess::NormalExit || p.exitCode() != 0) {
        throw std::runtime_error(p.readAllStandardError().data());
    }
}

void DcdClient::complete(const QString &filePath, int position, CompletionList &result)
{
    DEBUG_GUARD("");
    QStringList args = m_portArguments;
    args << QLatin1String("-c") + QString::number(position) << filePath;
    QProcess process;
    startProcess(process, m_processName, args, m_filePath);
    waitForFinished(process);
    QByteArray array(process.readAllStandardOutput());
    return parseOutput(array, result);
}

void DcdClient::completeFromArray(const QString &array, int position, DcdClient::CompletionList &result)
{
    DEBUG_GUARD(QString::number(position));
    QStringList args = m_portArguments;
    args << QLatin1String("-c") + QString::number(position);
    QProcess process;
    startProcess(process, m_processName, args, m_filePath);
    process.write(array.toLatin1());
    if (!process.waitForBytesWritten(5000)) {
        throw std::runtime_error("process writing data timeout");
    }
    process.closeWriteChannel();
    waitForFinished(process);
    QByteArray output(process.readAllStandardOutput());
    return parseOutput(output, result);
}

void DcdClient::appendIncludePath(const QString &includePath)
{
    DEBUG_GUARD("");
    QStringList args = m_portArguments;
    args << QLatin1String("-I") + includePath;
    QProcess process;
    startProcess(process, m_processName, args, m_filePath);
    waitForFinished(process);
}

void DcdClient::findSymbolLocation(const QString &array, int position, DcdClient::Location &result)
{
    DEBUG_GUARD(QString::number(position));
    QStringList args = m_portArguments;
    args << QLatin1String("-c") + QString::number(position) << "-l";
    QProcess process;
    startProcess(process, m_processName, args, m_filePath);
    process.write(array.toLatin1());
    if (!process.waitForBytesWritten(2000)) {
        throw std::runtime_error("process writing data timeout");
    }
    process.closeWriteChannel();
    waitForFinished(process);
    QString str(process.readAllStandardOutput());
    QStringList list = str.split('\t');
    result = list.size() == 2 ? Location(list.front(), list.back().toInt()) : Location(QString(), 0);
}

void DcdClient::getDocumentationComments(const QString &array, int position, QStringList &result)
{
    DEBUG_GUARD(QString::number(position));
    QStringList args = m_portArguments;
    args << QLatin1String("-c") + QString::number(position) << "-d";
    QProcess process;
    startProcess(process, m_processName, args, m_filePath);
    process.write(array.toLatin1());
    if (!process.waitForBytesWritten(2000)) {
        throw std::runtime_error("process writing data timeout");
    }
    process.closeWriteChannel();
    waitForFinished(process);
    QString str(process.readAllStandardOutput());
    result = str.split('\n');
}

void DcdClient::parseOutput(const QByteArray &output, DcdClient::CompletionList &result)
{
    result.list.clear();
    result.type = DCD_BAD_TYPE;
    QTextStream stream(output);
    QString line = stream.readLine();
    if (line == QLatin1String("identifiers")) {
        return parseIdentifiers(stream, result);
    } else if (line == QLatin1String("calltips")) {
        return parseCalltips(stream, result);
    } else if (line.isEmpty()) {
        return;
    } else {
        throw std::runtime_error("unknown output type");
    }
}

void DcdClient::parseIdentifiers(QTextStream &stream, DcdClient::CompletionList &result)
{
    QString line;
    do {
           line = stream.readLine();
           if (line.isNull() || line.isEmpty()) break;
           QStringList tokens = line.split(QLatin1Char('\t'));
           if (tokens.size() != 2) {
               throw std::runtime_error("Failed to parse identifiers");
           }
           result.type = DCD_IDENTIFIER;
           result.list.push_back(DcdCompletion());
           result.list.back().data = tokens.front();
           result.list.back().type = DcdCompletion::fromString(tokens.back());
    } while (stream.status() == QTextStream::Ok);
}

void DcdClient::parseCalltips(QTextStream &stream, DcdClient::CompletionList &result)
{
    QString line;
    do {
           line = stream.readLine();
           if (line.isNull() || line.isEmpty()) break;
           result.type = DCD_CALLTIP;
           result.list.push_back(DcdCompletion());
           result.list.back().data = line;
           result.list.back().type = DcdCompletion::DCD_NO_TYPE;
    } while (stream.status() == QTextStream::Ok);
}

DcdCompletion::IdentifierType DcdCompletion::fromString(const QString &name)
{
    char c = name.at(0).toLatin1();
    switch (c) {
    case 'c': return DCD_CLASS;
    case 'i': return DCD_INTERFACE;
    case 's': return DCD_STRUCT;
    case 'u': return DCD_UNION;
    case 'v': return DCD_VAR;
    case 'm': return DCD_MEMBER_VAR;
    case 'k': return DCD_KEYWORD;
    case 'f': return DCD_FUNCTION;
    case 'g': return DCD_ENUM_NAME;
    case 'e': return DCD_ENUM_VAR;
    case 'P': return DCD_PACKAGE;
    case 'M': return DCD_MODULE;
    case 'a': return DCD_ARRAY;
    case 'A': return DCD_ASSOC_ARRAY;
    case 'l': return DCD_ALIAS;
    case 't': return DCD_TEMPLATE;
    case 'T': return DCD_MIXIN;
    default: return DCD_NO_TYPE;
    }
    return DCD_NO_TYPE;
}

DcdServer::DcdServer(const QString& projectName, const QString &processName, int port, QObject *parent)
    : QObject(parent), m_projectName(projectName), m_port(port), m_processName(processName)
{
    m_process = new QProcess(this);
    connect(m_process, SIGNAL(finished(int)), this, SLOT(onFinished(int)));
    connect(m_process, SIGNAL(error(QProcess::ProcessError)), this, SLOT(onError(QProcess::ProcessError)));
}

DcdServer::~DcdServer()
{
    DEBUG_GUARD("");
    stop();
    m_process->waitForFinished(10000);
}

int DcdServer::port() const
{
    return m_port;
}

const QString &DcdServer::projectName() const
{
    return m_projectName;
}

void DcdServer::setOutputFile(const QString &filePath)
{
    m_filePath = filePath;
}

void DcdServer::start()
{
    startProcess(*m_process, m_processName, QStringList() << QLatin1String("--port") << QString::number(m_port), m_filePath);
}

void DcdServer::stop()
{
    m_process->kill();
}

bool DcdServer::isRunning() const
{
    return (m_process && m_process->state() == QProcess::Running);
}

void DcdServer::onFinished(int errorCode)
{
    qDebug() << "DCD server finished";
    if (errorCode != 0) {
        emit error(tr("DCD server process has been terminated with exit code %1").arg(errorCode));
        qWarning("DCD server: %s", static_cast<QProcess*>(sender())->readAllStandardError().data());
    }
}

void DcdServer::onError(QProcess::ProcessError error)
{
    qDebug() << "DCD server error";
    switch (error) {
    case QProcess::FailedToStart:
        emit this->error(tr("DCD server failed to start"));
        break;
    case QProcess::Crashed:
        emit this->error(tr("DCD server crashed"));
        break;
    case QProcess::Timedout:
        emit this->error(tr("DCD server starting timeout"));
        break;
    default:
        emit this->error(tr("DCD server unknown error"));
        break;
    }
    stop();
}

// Factory
DcdFactory::ClientPointer DcdFactory::client(const QString &projectName)
{
    try {
        MapString::iterator it = mapChannels.find(projectName);
        if (it == mapChannels.end()) {
            int port =  m_firstPort + currentPortOffset % (m_lastPort - m_firstPort + 1);
            ServerPointer server(new Dcd::DcdServer(projectName, DlangEditor::DlangOptionsPage::dcdServerExecutable(), port, this));
            server->setOutputFile(DlangEditor::DlangOptionsPage::dcdServerLogPath());
            server->start();
            connect(server.data(), SIGNAL(error(QString)), this, SLOT(onError(QString)));
            ClientPointer client(new Dcd::DcdClient(projectName, DlangEditor::DlangOptionsPage::dcdClientExecutable(), port, this));
            appendIncludePaths(client);

            it = mapChannels.insert(projectName, qMakePair(client, server));
            ++currentPortOffset;
        } else {
            if (!it.value().second->isRunning()) {
                it.value().second->stop();
                mapChannels.erase(it);
                return ClientPointer();
            }
        }
        return it.value().first;
    } catch (std::exception &ex) {
        qDebug("Client exception: %s", ex.what());
    } catch (...) {
        qDebug("Client exception: unknown");
    }
    return ClientPointer();
}

void DcdFactory::appendIncludePaths(ClientPointer client)
{
    // append default include paths from options page
    QStringList list = DlangEditor::DlangOptionsPage::includePaths();

    // append include paths from project settings
    CppTools::CppModelManager *modelmanager =
            CppTools::CppModelManager::instance();
    if (modelmanager) {
        ProjectExplorer::Project *currentProject = ProjectExplorer::ProjectExplorerPlugin::currentProject();
        if (currentProject) {
            CppTools::ProjectInfo pinfo = modelmanager->projectInfo(currentProject);
            if (pinfo.isValid()) {
                foreach (const CppTools::ProjectPart::HeaderPath &header, pinfo.headerPaths()) {
                    if (header.isValid()) {
                        list.push_back(header.path);
                    }
                }
            }
        }
    }
    list.removeDuplicates();

    foreach (const QString& l, list) {
        client->appendIncludePath(l);
    }
}

void DcdFactory::setPortRange(int first, int last)
{
    m_firstPort = first;
    m_lastPort = std::max(last, first);
}

QPair<int, int> DcdFactory::portRange() const
{
    return qMakePair(m_firstPort, m_lastPort);
}

DcdFactory *DcdFactory::instance()
{
    static DcdFactory inst(DlangEditor::DlangOptionsPage::portsRange());
    return &inst;
}

void DcdFactory::onError(QString error)
{
    qDebug("DcdFactory::onError: %s", error.toStdString().data());
    qWarning("DcdFactory::onError: %s", error.toStdString().data());
    Dcd::DcdServer *server = qobject_cast<Dcd::DcdServer*>(sender());
    server->stop();
    mapChannels.remove(server->projectName());
}

DcdFactory::DcdFactory(QPair<int, int> range)
    : currentPortOffset(0)
{
    setPortRange(range.first, range.second);
}
<commit_msg>Dcd processes extra logging *Process arguments logging<commit_after>#include "dcdsupport.h"

#include "dlangdebughelper.h"
#include <dlangoptionspage.h>

#include <stdexcept>

#include <QProcess>
#include <QTextStream>
#include <QDebug>

#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/project.h>
#include <cpptools/cppmodelmanager.h>

using namespace Dcd;

DcdClient::DcdClient(const QString &projectName, const QString &processName, int port, QObject *parent)
    : QObject(parent), m_projectName(projectName), m_port(port), m_processName(processName)
{
    m_portArguments << QLatin1String("--port") << QString::number(port);
}

const QString &DcdClient::projectName() const
{
    return m_projectName;
}

void DcdClient::setOutputFile(const QString &filePath)
{
    m_filePath = filePath;
}

void startProcess(QProcess &p, const QString &processName, const QStringList &args,
                  const QString &filePath, QIODevice::OpenMode mode = QIODevice::ReadWrite)
{
    if (p.state() != QProcess::NotRunning) {
        throw std::runtime_error("process is already running");
    }
    if (!filePath.isEmpty()) {
        p.setStandardOutputFile(filePath, QIODevice::Append | QIODevice::Unbuffered);
        p.setStandardErrorFile(filePath, QIODevice::Append | QIODevice::Unbuffered);
    }
    qDebug() << processName << " process " << args;
    p.start(processName, args, mode);
    if (!p.waitForStarted(1000)) {
        throw std::runtime_error("process start timeout");
    }
}

void waitForFinished(QProcess &p)
{
    if (!p.waitForFinished(1000)) {
        throw std::runtime_error("process finish timeout");
    }
    if (p.exitStatus() != QProcess::NormalExit || p.exitCode() != 0) {
        throw std::runtime_error(p.readAllStandardError().data());
    }
}

void DcdClient::complete(const QString &filePath, int position, CompletionList &result)
{
    DEBUG_GUARD("");
    QStringList args = m_portArguments;
    args << QLatin1String("-c") + QString::number(position) << filePath;
    qDebug() << "dcd-client process " << args;
    QProcess process;
    startProcess(process, m_processName, args, m_filePath);
    waitForFinished(process);
    QByteArray array(process.readAllStandardOutput());
    return parseOutput(array, result);
}

void DcdClient::completeFromArray(const QString &array, int position, DcdClient::CompletionList &result)
{
    DEBUG_GUARD(QString::number(position));
    QStringList args = m_portArguments;
    args << QLatin1String("-c") + QString::number(position);
    QProcess process;
    startProcess(process, m_processName, args, m_filePath);
    process.write(array.toLatin1());
    if (!process.waitForBytesWritten(5000)) {
        throw std::runtime_error("process writing data timeout");
    }
    process.closeWriteChannel();
    waitForFinished(process);
    QByteArray output(process.readAllStandardOutput());
    return parseOutput(output, result);
}

void DcdClient::appendIncludePath(const QString &includePath)
{
    DEBUG_GUARD("");
    QStringList args = m_portArguments;
    args << QLatin1String("-I") + includePath;
    qDebug() << "dcd-client process " << args;
    QProcess process;
    startProcess(process, m_processName, args, m_filePath);
    waitForFinished(process);
}

void DcdClient::findSymbolLocation(const QString &array, int position, DcdClient::Location &result)
{
    DEBUG_GUARD(QString::number(position));
    QStringList args = m_portArguments;
    args << QLatin1String("-c") + QString::number(position) << "-l";
    qDebug() << "dcd-client process " << args;
    QProcess process;
    startProcess(process, m_processName, args, m_filePath);
    process.write(array.toLatin1());
    if (!process.waitForBytesWritten(2000)) {
        throw std::runtime_error("process writing data timeout");
    }
    process.closeWriteChannel();
    waitForFinished(process);
    QString str(process.readAllStandardOutput());
    QStringList list = str.split('\t');
    result = list.size() == 2 ? Location(list.front(), list.back().toInt()) : Location(QString(), 0);
}

void DcdClient::getDocumentationComments(const QString &array, int position, QStringList &result)
{
    DEBUG_GUARD(QString::number(position));
    QStringList args = m_portArguments;
    args << QLatin1String("-c") + QString::number(position) << "-d";
    qDebug() << "dcd-client process " << args;
    QProcess process;
    startProcess(process, m_processName, args, m_filePath);
    process.write(array.toLatin1());
    if (!process.waitForBytesWritten(2000)) {
        throw std::runtime_error("process writing data timeout");
    }
    process.closeWriteChannel();
    waitForFinished(process);
    QString str(process.readAllStandardOutput());
    result = str.split('\n');
}

void DcdClient::parseOutput(const QByteArray &output, DcdClient::CompletionList &result)
{
    result.list.clear();
    result.type = DCD_BAD_TYPE;
    QTextStream stream(output);
    QString line = stream.readLine();
    if (line == QLatin1String("identifiers")) {
        return parseIdentifiers(stream, result);
    } else if (line == QLatin1String("calltips")) {
        return parseCalltips(stream, result);
    } else if (line.isEmpty()) {
        return;
    } else {
        throw std::runtime_error("unknown output type");
    }
}

void DcdClient::parseIdentifiers(QTextStream &stream, DcdClient::CompletionList &result)
{
    QString line;
    do {
           line = stream.readLine();
           if (line.isNull() || line.isEmpty()) break;
           QStringList tokens = line.split(QLatin1Char('\t'));
           if (tokens.size() != 2) {
               throw std::runtime_error("Failed to parse identifiers");
           }
           result.type = DCD_IDENTIFIER;
           result.list.push_back(DcdCompletion());
           result.list.back().data = tokens.front();
           result.list.back().type = DcdCompletion::fromString(tokens.back());
    } while (stream.status() == QTextStream::Ok);
}

void DcdClient::parseCalltips(QTextStream &stream, DcdClient::CompletionList &result)
{
    QString line;
    do {
           line = stream.readLine();
           if (line.isNull() || line.isEmpty()) break;
           result.type = DCD_CALLTIP;
           result.list.push_back(DcdCompletion());
           result.list.back().data = line;
           result.list.back().type = DcdCompletion::DCD_NO_TYPE;
    } while (stream.status() == QTextStream::Ok);
}

DcdCompletion::IdentifierType DcdCompletion::fromString(const QString &name)
{
    char c = name.at(0).toLatin1();
    switch (c) {
    case 'c': return DCD_CLASS;
    case 'i': return DCD_INTERFACE;
    case 's': return DCD_STRUCT;
    case 'u': return DCD_UNION;
    case 'v': return DCD_VAR;
    case 'm': return DCD_MEMBER_VAR;
    case 'k': return DCD_KEYWORD;
    case 'f': return DCD_FUNCTION;
    case 'g': return DCD_ENUM_NAME;
    case 'e': return DCD_ENUM_VAR;
    case 'P': return DCD_PACKAGE;
    case 'M': return DCD_MODULE;
    case 'a': return DCD_ARRAY;
    case 'A': return DCD_ASSOC_ARRAY;
    case 'l': return DCD_ALIAS;
    case 't': return DCD_TEMPLATE;
    case 'T': return DCD_MIXIN;
    default: return DCD_NO_TYPE;
    }
    return DCD_NO_TYPE;
}

DcdServer::DcdServer(const QString& projectName, const QString &processName, int port, QObject *parent)
    : QObject(parent), m_projectName(projectName), m_port(port), m_processName(processName)
{
    m_process = new QProcess(this);
    connect(m_process, SIGNAL(finished(int)), this, SLOT(onFinished(int)));
    connect(m_process, SIGNAL(error(QProcess::ProcessError)), this, SLOT(onError(QProcess::ProcessError)));
}

DcdServer::~DcdServer()
{
    DEBUG_GUARD("");
    stop();
    m_process->waitForFinished(10000);
}

int DcdServer::port() const
{
    return m_port;
}

const QString &DcdServer::projectName() const
{
    return m_projectName;
}

void DcdServer::setOutputFile(const QString &filePath)
{
    m_filePath = filePath;
}

void DcdServer::start()
{
    startProcess(*m_process, m_processName, QStringList() << QLatin1String("--port") << QString::number(m_port), m_filePath);
}

void DcdServer::stop()
{
    m_process->kill();
}

bool DcdServer::isRunning() const
{
    return (m_process && m_process->state() == QProcess::Running);
}

void DcdServer::onFinished(int errorCode)
{
    qDebug() << "DCD server finished";
    if (errorCode != 0) {
        emit error(tr("DCD server process has been terminated with exit code %1").arg(errorCode));
        qWarning("DCD server: %s", static_cast<QProcess*>(sender())->readAllStandardError().data());
    }
}

void DcdServer::onError(QProcess::ProcessError error)
{
    qDebug() << "DCD server error";
    switch (error) {
    case QProcess::FailedToStart:
        emit this->error(tr("DCD server failed to start"));
        break;
    case QProcess::Crashed:
        emit this->error(tr("DCD server crashed"));
        break;
    case QProcess::Timedout:
        emit this->error(tr("DCD server starting timeout"));
        break;
    default:
        emit this->error(tr("DCD server unknown error"));
        break;
    }
    stop();
}

// Factory
DcdFactory::ClientPointer DcdFactory::client(const QString &projectName)
{
    try {
        MapString::iterator it = mapChannels.find(projectName);
        if (it == mapChannels.end()) {
            int port =  m_firstPort + currentPortOffset % (m_lastPort - m_firstPort + 1);
            ServerPointer server(new Dcd::DcdServer(projectName, DlangEditor::DlangOptionsPage::dcdServerExecutable(), port, this));
            server->setOutputFile(DlangEditor::DlangOptionsPage::dcdServerLogPath());
            server->start();
            connect(server.data(), SIGNAL(error(QString)), this, SLOT(onError(QString)));
            ClientPointer client(new Dcd::DcdClient(projectName, DlangEditor::DlangOptionsPage::dcdClientExecutable(), port, this));
            appendIncludePaths(client);

            it = mapChannels.insert(projectName, qMakePair(client, server));
            ++currentPortOffset;
        } else {
            if (!it.value().second->isRunning()) {
                it.value().second->stop();
                mapChannels.erase(it);
                return ClientPointer();
            }
        }
        return it.value().first;
    } catch (std::exception &ex) {
        qDebug("Client exception: %s", ex.what());
    } catch (...) {
        qDebug("Client exception: unknown");
    }
    return ClientPointer();
}

void DcdFactory::appendIncludePaths(ClientPointer client)
{
    // append default include paths from options page
    QStringList list = DlangEditor::DlangOptionsPage::includePaths();

    // append include paths from project settings
    CppTools::CppModelManager *modelmanager =
            CppTools::CppModelManager::instance();
    if (modelmanager) {
        ProjectExplorer::Project *currentProject = ProjectExplorer::ProjectExplorerPlugin::currentProject();
        if (currentProject) {
            CppTools::ProjectInfo pinfo = modelmanager->projectInfo(currentProject);
            if (pinfo.isValid()) {
                foreach (const CppTools::ProjectPart::HeaderPath &header, pinfo.headerPaths()) {
                    if (header.isValid()) {
                        list.push_back(header.path);
                    }
                }
            }
        }
    }
    list.removeDuplicates();

    foreach (const QString& l, list) {
        client->appendIncludePath(l);
    }
}

void DcdFactory::setPortRange(int first, int last)
{
    m_firstPort = first;
    m_lastPort = std::max(last, first);
}

QPair<int, int> DcdFactory::portRange() const
{
    return qMakePair(m_firstPort, m_lastPort);
}

DcdFactory *DcdFactory::instance()
{
    static DcdFactory inst(DlangEditor::DlangOptionsPage::portsRange());
    return &inst;
}

void DcdFactory::onError(QString error)
{
    qDebug("DcdFactory::onError: %s", error.toStdString().data());
    qWarning("DcdFactory::onError: %s", error.toStdString().data());
    Dcd::DcdServer *server = qobject_cast<Dcd::DcdServer*>(sender());
    server->stop();
    mapChannels.remove(server->projectName());
}

DcdFactory::DcdFactory(QPair<int, int> range)
    : currentPortOffset(0)
{
    setPortRange(range.first, range.second);
}
<|endoftext|>


<commit_before>#ifdef _WIN32

#include <assert.h>

#include "win_fsnotifier.h"

using namespace std;

//
// WatchPoint
//

WatchPoint::WatchPoint(Server* server, const u16string& path)
    : path(path) {
    wstring pathW(path.begin(), path.end());
    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:", path, GetLastError());
    }
    this->directoryHandle = directoryHandle;

    this->server = server;
    this->buffer.reserve(EVENT_BUFFER_SIZE);
    ZeroMemory(&this->overlapped, sizeof(OVERLAPPED));
    this->overlapped.hEvent = this;
    listen();
}

WatchPoint::~WatchPoint() {
    BOOL ret = CloseHandle(directoryHandle);
    if (!ret) {
        log_severe(server->getThreadEnv(), "Couldn't close handle %p for '%ls': %d", directoryHandle, path.c_str(), GetLastError());
    }
}

void WatchPoint::close() {
    BOOL ret = CancelIo(directoryHandle);
    if (!ret) {
        log_severe(server->getThreadEnv(), "Couldn't cancel I/O %p for '%ls': %d", directoryHandle, path.c_str(), GetLastError());
    }

    // Force the processing of the ERROR_OPERATION_ABORTED event generated by CancelIO() (see handleEventCallback())
    // Without this the abort event is handled after close() returns
    // Which means we'll unlock the calling thread before the watch point is truly cleaned up
    SleepEx(0, true);
}

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

void WatchPoint::listen() {
    BOOL success = ReadDirectoryChangesW(
        directoryHandle,        // handle to directory
        &buffer[0],             // read results buffer
        EVENT_BUFFER_SIZE,      // length of buffer
        TRUE,                   // include children
        EVENT_MASK,             // filter conditions
        NULL,                   // bytes returned
        &overlapped,            // overlapped buffer
        &handleEventCallback    // completion routine
    );
    if (!success) {
        throw FileWatcherException("Couldn't start watching", path, GetLastError());
    }
}

void WatchPoint::handleEventsInBuffer(DWORD errorCode, DWORD bytesTransferred) {
    server->handleEvents(this, errorCode, buffer, bytesTransferred);
}

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

    try {
        if (errorCode == ERROR_OPERATION_ABORTED) {
            log_fine(env, "Finished watching '%s'", utf16ToUtf8String(path).c_str());
            reportFinished(path);
            return;
        }
        // TODO Handle other error codes

        if (bytesTransferred == 0) {
            // Got a buffer overflow => current changes lost => send INVALIDATE on root
            log_info(env, "Detected overflow for %s", utf16ToUtf8String(path).c_str());
            reportChange(env, FILE_EVENT_INVALIDATE, path);
        } else {
            int index = 0;
            for (;;) {
                FILE_NOTIFY_INFORMATION* current = (FILE_NOTIFY_INFORMATION*) &buffer[index];
                handleEvent(env, path, current);
                if (current->NextEntryOffset == 0) {
                    break;
                }
                index += current->NextEntryOffset;
            }
        }

        watchPoint->listen();
    } catch (const exception& ex) {
        reportError(env, ex);
        reportFinished(path);
    }
}

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

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

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

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

// TODO How can this be done nicer, wihtout both unnecessary copy and in-place mutation?
void convertToLongPathIfNeeded(u16string& 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, u"\\\\?\\");
    } 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, u"\\\\?\\UNC\\");
    } else {
        // It is some sort of unknown format, don't mess with it
    }
}

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

    log_fine(env, "Change detected: 0x%x '%ls'", info->Action, changedPathW.c_str());

    jint type;
    if (info->Action == FILE_ACTION_ADDED || info->Action == FILE_ACTION_RENAMED_NEW_NAME) {
        type = FILE_EVENT_CREATED;
    } else if (info->Action == FILE_ACTION_REMOVED || info->Action == FILE_ACTION_RENAMED_OLD_NAME) {
        type = FILE_EVENT_REMOVED;
    } else if (info->Action == FILE_ACTION_MODIFIED) {
        type = FILE_EVENT_MODIFIED;
    } else {
        log_warning(env, "Unknown event 0x%x for %ls", info->Action, changedPathW.c_str());
        type = FILE_EVENT_UNKNOWN;
    }

    reportChange(env, type, changedPath);
}

//
// Server
//

Server::Server(JNIEnv* env, jobject watcherCallback)
    : AbstractServer(env, watcherCallback) {
    startThread();
    // TODO Error handling
    SetThreadPriority(this->watcherThread.native_handle(), THREAD_PRIORITY_ABOVE_NORMAL);
}

void Server::terminate() {
    terminated = true;
}

Server::~Server() {
    list<u16string> paths;
    for (auto& watchPoint : watchPoints) {
        paths.push_back(watchPoint.first);
    }
    for (auto& path : paths) {
        executeOnThread(shared_ptr<Command>(new UnregisterPathCommand(path)));
    }
    executeOnThread(shared_ptr<Command>(new TerminateCommand()));

    if (watcherThread.joinable()) {
        watcherThread.join();
    }
}

void Server::runLoop(JNIEnv* env, function<void(exception_ptr)> notifyStarted) {
    notifyStarted(nullptr);

    while (!terminated || watchPoints.size() > 0) {
        SleepEx(INFINITE, true);
    }
}

static void CALLBACK processCommandsCallback(_In_ ULONG_PTR info) {
    Server* server = (Server*) info;
    server->processCommands();
}

void Server::processCommandsOnThread() {
    QueueUserAPC(processCommandsCallback, watcherThread.native_handle(), (ULONG_PTR) this);
}

void Server::registerPath(const u16string& path) {
    u16string longPath = path;
    convertToLongPathIfNeeded(longPath);
    if (watchPoints.find(longPath) != watchPoints.end()) {
        throw FileWatcherException("Already watching path", path);
    }
    watchPoints.emplace(piecewise_construct,
        forward_as_tuple(longPath),
        forward_as_tuple(this, longPath));
}

void Server::unregisterPath(const u16string& path) {
    u16string longPath = path;
    convertToLongPathIfNeeded(longPath);
    auto it = watchPoints.find(longPath);
    if (it == watchPoints.end()) {
        log_fine(getThreadEnv(), "Path is not watched: %s", utf16ToUtf8String(path).c_str());
        return;
    }
    it->second.close();
}

void Server::reportFinished(const u16string path) {
    watchPoints.erase(path);
}

//
// JNI calls
//

JNIEXPORT jobject JNICALL
Java_net_rubygrapefruit_platform_internal_jni_WindowsFileEventFunctions_startWatcher(JNIEnv* env, jclass target, jobject javaCallback) {
    return wrapServer(env, [env, javaCallback]() {
        return new Server(env, javaCallback);
    });
}

#endif
<commit_msg>Win: Handle all OS errors during event handling<commit_after>#ifdef _WIN32

#include <assert.h>

#include "win_fsnotifier.h"

using namespace std;

//
// WatchPoint
//

WatchPoint::WatchPoint(Server* server, const u16string& path)
    : path(path) {
    wstring pathW(path.begin(), path.end());
    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:", path, GetLastError());
    }
    this->directoryHandle = directoryHandle;

    this->server = server;
    this->buffer.reserve(EVENT_BUFFER_SIZE);
    ZeroMemory(&this->overlapped, sizeof(OVERLAPPED));
    this->overlapped.hEvent = this;
    listen();
}

WatchPoint::~WatchPoint() {
    BOOL ret = CloseHandle(directoryHandle);
    if (!ret) {
        log_severe(server->getThreadEnv(), "Couldn't close handle %p for '%ls': %d", directoryHandle, path.c_str(), GetLastError());
    }
}

void WatchPoint::close() {
    BOOL ret = CancelIo(directoryHandle);
    if (!ret) {
        log_severe(server->getThreadEnv(), "Couldn't cancel I/O %p for '%ls': %d", directoryHandle, path.c_str(), GetLastError());
    }

    // Force the processing of the ERROR_OPERATION_ABORTED event generated by CancelIO() (see handleEventCallback())
    // Without this the abort event is handled after close() returns
    // Which means we'll unlock the calling thread before the watch point is truly cleaned up
    SleepEx(0, true);
}

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

void WatchPoint::listen() {
    BOOL success = ReadDirectoryChangesW(
        directoryHandle,        // handle to directory
        &buffer[0],             // read results buffer
        EVENT_BUFFER_SIZE,      // length of buffer
        TRUE,                   // include children
        EVENT_MASK,             // filter conditions
        NULL,                   // bytes returned
        &overlapped,            // overlapped buffer
        &handleEventCallback    // completion routine
    );
    if (!success) {
        throw FileWatcherException("Couldn't start watching", path, GetLastError());
    }
}

void WatchPoint::handleEventsInBuffer(DWORD errorCode, DWORD bytesTransferred) {
    server->handleEvents(this, errorCode, buffer, bytesTransferred);
}

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

    try {
        switch (errorCode) {
            case ERROR_SUCCESS:
                break;
            case ERROR_OPERATION_ABORTED:
                log_fine(env, "Finished watching '%s'", utf16ToUtf8String(path).c_str());
                reportFinished(path);
                return;
            default:
                throw FileWatcherException("Error received when handling events", errorCode);
        }

        if (bytesTransferred == 0) {
            // Got a buffer overflow => current changes lost => send INVALIDATE on root
            log_info(env, "Detected overflow for %s", utf16ToUtf8String(path).c_str());
            reportChange(env, FILE_EVENT_INVALIDATE, path);
        } else {
            int index = 0;
            for (;;) {
                FILE_NOTIFY_INFORMATION* current = (FILE_NOTIFY_INFORMATION*) &buffer[index];
                handleEvent(env, path, current);
                if (current->NextEntryOffset == 0) {
                    break;
                }
                index += current->NextEntryOffset;
            }
        }

        watchPoint->listen();
    } catch (const exception& ex) {
        reportError(env, ex);
        reportFinished(path);
    }
}

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

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

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

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

// TODO How can this be done nicer, wihtout both unnecessary copy and in-place mutation?
void convertToLongPathIfNeeded(u16string& 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, u"\\\\?\\");
    } 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, u"\\\\?\\UNC\\");
    } else {
        // It is some sort of unknown format, don't mess with it
    }
}

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

    log_fine(env, "Change detected: 0x%x '%ls'", info->Action, changedPathW.c_str());

    jint type;
    if (info->Action == FILE_ACTION_ADDED || info->Action == FILE_ACTION_RENAMED_NEW_NAME) {
        type = FILE_EVENT_CREATED;
    } else if (info->Action == FILE_ACTION_REMOVED || info->Action == FILE_ACTION_RENAMED_OLD_NAME) {
        type = FILE_EVENT_REMOVED;
    } else if (info->Action == FILE_ACTION_MODIFIED) {
        type = FILE_EVENT_MODIFIED;
    } else {
        log_warning(env, "Unknown event 0x%x for %ls", info->Action, changedPathW.c_str());
        type = FILE_EVENT_UNKNOWN;
    }

    reportChange(env, type, changedPath);
}

//
// Server
//

Server::Server(JNIEnv* env, jobject watcherCallback)
    : AbstractServer(env, watcherCallback) {
    startThread();
    // TODO Error handling
    SetThreadPriority(this->watcherThread.native_handle(), THREAD_PRIORITY_ABOVE_NORMAL);
}

void Server::terminate() {
    terminated = true;
}

Server::~Server() {
    list<u16string> paths;
    for (auto& watchPoint : watchPoints) {
        paths.push_back(watchPoint.first);
    }
    for (auto& path : paths) {
        executeOnThread(shared_ptr<Command>(new UnregisterPathCommand(path)));
    }
    executeOnThread(shared_ptr<Command>(new TerminateCommand()));

    if (watcherThread.joinable()) {
        watcherThread.join();
    }
}

void Server::runLoop(JNIEnv* env, function<void(exception_ptr)> notifyStarted) {
    notifyStarted(nullptr);

    while (!terminated || watchPoints.size() > 0) {
        SleepEx(INFINITE, true);
    }
}

static void CALLBACK processCommandsCallback(_In_ ULONG_PTR info) {
    Server* server = (Server*) info;
    server->processCommands();
}

void Server::processCommandsOnThread() {
    QueueUserAPC(processCommandsCallback, watcherThread.native_handle(), (ULONG_PTR) this);
}

void Server::registerPath(const u16string& path) {
    u16string longPath = path;
    convertToLongPathIfNeeded(longPath);
    if (watchPoints.find(longPath) != watchPoints.end()) {
        throw FileWatcherException("Already watching path", path);
    }
    watchPoints.emplace(piecewise_construct,
        forward_as_tuple(longPath),
        forward_as_tuple(this, longPath));
}

void Server::unregisterPath(const u16string& path) {
    u16string longPath = path;
    convertToLongPathIfNeeded(longPath);
    auto it = watchPoints.find(longPath);
    if (it == watchPoints.end()) {
        log_fine(getThreadEnv(), "Path is not watched: %s", utf16ToUtf8String(path).c_str());
        return;
    }
    it->second.close();
}

void Server::reportFinished(const u16string path) {
    watchPoints.erase(path);
}

//
// JNI calls
//

JNIEXPORT jobject JNICALL
Java_net_rubygrapefruit_platform_internal_jni_WindowsFileEventFunctions_startWatcher(JNIEnv* env, jclass target, jobject javaCallback) {
    return wrapServer(env, [env, javaCallback]() {
        return new Server(env, javaCallback);
    });
}

#endif
<|endoftext|>


<commit_before>/*****************************************************************************
 * Media Library
 *****************************************************************************
 * Copyright (C) 2020 Hugo Beauzée-Luyssen, Videolabs, VideoLAN
 *
 * Authors: Hugo Beauzée-Luyssen <hugo@beauzee.fr>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

#if HAVE_CONFIG_H
# include "config.h"
#endif

#include "filesystem/libvlc/DeviceLister.h"
#include "utils/VLCInstance.h"
#include "utils/Filename.h"
#include "utils/Url.h"
#include "filesystem/libvlc/Device.h"

#include "logging/Logger.h"

namespace medialibrary
{
namespace fs
{
namespace libvlc
{

DeviceLister::DeviceLister( const std::string &protocol )
    : m_protocol( protocol )
    , m_cb( nullptr )
{
}

void DeviceLister::refresh()
{
    /*
     * We are continuously refreshing through libvlc's discoverer so there's
     * nothing particular to do here
     */
}

bool DeviceLister::start( IDeviceListerCb *cb )
{
    assert( m_cb == nullptr );
    m_cb = cb;
    auto started = false;

    for ( auto& sd : m_sds )
    {
        if ( sd.name == "upnp" || sd.name == "sap" )
            continue;
        auto& em = sd.discoverer.mediaList()->eventManager();
        em.onItemAdded( [this]( VLC::MediaPtr m, int ) { onDeviceAdded( std::move( m ) ); } );
        em.onItemDeleted( [this]( VLC::MediaPtr m, int ) { onDeviceRemoved( std::move( m ) ); } );
        LOG_DEBUG( "Starting Service Discovery ", sd.name );
        if ( sd.discoverer.start() == false )
            LOG_WARN( "Failed to start SD ", sd.name );
        else if ( started == false )
            started = true;
    }

    return started;
}

void DeviceLister::stop()
{
    for ( auto& sd : m_sds )
    {
        if ( sd.discoverer.isRunning() == true )
            sd.discoverer.stop();
    }
}

void DeviceLister::addSD( const std::string& name )
{
    m_sds.push_back( SD{
        name,
        VLC::MediaDiscoverer{ VLCInstance::get(), name }
    } );
}

void DeviceLister::onDeviceAdded( VLC::MediaPtr media )
{
    const auto& mrl = media->mrl();
    if ( utils::url::scheme( mrl ) != m_protocol )
        return;

    auto uuid = media->meta( libvlc_meta_Title );
    LOG_DEBUG( "Mountpoint added: ", mrl, " from device ", uuid );
    m_cb->onDeviceMounted( uuid, utils::file::toFolderPath( mrl ), true );
}

void DeviceLister::onDeviceRemoved( VLC::MediaPtr media )
{
    const auto& mrl = media->mrl();
    if ( utils::url::scheme( mrl ) != m_protocol )
        return;

    auto uuid = media->meta( libvlc_meta_Title );
    LOG_DEBUG( "Mountpoint removed: ", mrl, " from device ", uuid );

    m_cb->onDeviceUnmounted( uuid, utils::file::toFolderPath( mrl ) );
}

}
}
}
<commit_msg>libvlc: DeviceLister: Log when stopping a SD<commit_after>/*****************************************************************************
 * Media Library
 *****************************************************************************
 * Copyright (C) 2020 Hugo Beauzée-Luyssen, Videolabs, VideoLAN
 *
 * Authors: Hugo Beauzée-Luyssen <hugo@beauzee.fr>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/

#if HAVE_CONFIG_H
# include "config.h"
#endif

#include "filesystem/libvlc/DeviceLister.h"
#include "utils/VLCInstance.h"
#include "utils/Filename.h"
#include "utils/Url.h"
#include "filesystem/libvlc/Device.h"

#include "logging/Logger.h"

namespace medialibrary
{
namespace fs
{
namespace libvlc
{

DeviceLister::DeviceLister( const std::string &protocol )
    : m_protocol( protocol )
    , m_cb( nullptr )
{
}

void DeviceLister::refresh()
{
    /*
     * We are continuously refreshing through libvlc's discoverer so there's
     * nothing particular to do here
     */
}

bool DeviceLister::start( IDeviceListerCb *cb )
{
    assert( m_cb == nullptr );
    m_cb = cb;
    auto started = false;

    for ( auto& sd : m_sds )
    {
        if ( sd.name == "upnp" || sd.name == "sap" )
            continue;
        auto& em = sd.discoverer.mediaList()->eventManager();
        em.onItemAdded( [this]( VLC::MediaPtr m, int ) { onDeviceAdded( std::move( m ) ); } );
        em.onItemDeleted( [this]( VLC::MediaPtr m, int ) { onDeviceRemoved( std::move( m ) ); } );
        LOG_DEBUG( "Starting Service Discovery ", sd.name );
        if ( sd.discoverer.start() == false )
            LOG_WARN( "Failed to start SD ", sd.name );
        else if ( started == false )
            started = true;
    }

    return started;
}

void DeviceLister::stop()
{
    for ( auto& sd : m_sds )
    {
        if ( sd.discoverer.isRunning() == true )
        {
            LOG_DEBUG( "Stopping Service Discovery ", sd.name );
            sd.discoverer.stop();
        }
    }
}

void DeviceLister::addSD( const std::string& name )
{
    m_sds.push_back( SD{
        name,
        VLC::MediaDiscoverer{ VLCInstance::get(), name }
    } );
}

void DeviceLister::onDeviceAdded( VLC::MediaPtr media )
{
    const auto& mrl = media->mrl();
    if ( utils::url::scheme( mrl ) != m_protocol )
        return;

    auto uuid = media->meta( libvlc_meta_Title );
    LOG_DEBUG( "Mountpoint added: ", mrl, " from device ", uuid );
    m_cb->onDeviceMounted( uuid, utils::file::toFolderPath( mrl ), true );
}

void DeviceLister::onDeviceRemoved( VLC::MediaPtr media )
{
    const auto& mrl = media->mrl();
    if ( utils::url::scheme( mrl ) != m_protocol )
        return;

    auto uuid = media->meta( libvlc_meta_Title );
    LOG_DEBUG( "Mountpoint removed: ", mrl, " from device ", uuid );

    m_cb->onDeviceUnmounted( uuid, utils::file::toFolderPath( mrl ) );
}

}
}
}
<|endoftext|>


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

#include "chrome/browser/notifications/sync_notifier/notification_bitmap_fetcher.h"

#include "base/compiler_specific.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/test/test_utils.h"
#include "net/http/http_status_code.h"
#include "net/url_request/test_url_fetcher_factory.h"
#include "net/url_request/url_fetcher.h"
#include "net/url_request/url_request_status.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/gfx/codec/png_codec.h"
#include "ui/gfx/size.h"
#include "ui/gfx/skia_util.h"

namespace {
const bool kAsyncCall = true;
const bool kSyncCall = false;
}  // namespace

namespace notifier {

// Class to catch events from the NotificationBitmapFetcher for testing.
class NotificationBitmapFetcherTestDelegate
    : public NotificationBitmapFetcherDelegate {
 public:
  explicit NotificationBitmapFetcherTestDelegate(bool async)
      : called_(false), success_(false), async_(async) {}

  virtual ~NotificationBitmapFetcherTestDelegate() {
    EXPECT_TRUE(called_);
  }

  // Method inherited from NotificationBitmapFetcherDelegate.
  virtual void OnFetchComplete(const GURL url,
                               const SkBitmap* bitmap) OVERRIDE {
    called_ = true;
    url_ = url;
    if (NULL != bitmap) {
      success_ = true;
      bitmap->deepCopyTo(&bitmap_, bitmap->getConfig());
    }
    // For async calls, we need to quit the message loop so the test can
    // continue.
    if (async_) {
      base::MessageLoop::current()->Quit();
    }
  }

  GURL url() const { return url_; }
  bool success() const { return success_; }
  const SkBitmap& bitmap() const { return bitmap_; }

 private:
  bool called_;
  GURL url_;
  bool success_;
  bool async_;
  SkBitmap bitmap_;

  DISALLOW_COPY_AND_ASSIGN(NotificationBitmapFetcherTestDelegate);
};

class NotificationBitmapFetcherBrowserTest : public InProcessBrowserTest {
 public:
  virtual void SetUp() OVERRIDE {
    url_fetcher_factory_.reset(new net::FakeURLFetcherFactory(NULL));
    InProcessBrowserTest::SetUp();
  }

 protected:
  scoped_ptr<net::FakeURLFetcherFactory> url_fetcher_factory_;
};

#if defined(OS_WIN)
#define MAYBE_StartTest DISABLED_StartTest
#else
#define MAYBE_StartTest StartTest
#endif

// WARNING:  These tests work with --single_process, but not
// --single-process.  The reason is that the sandbox does not get created
// for us by the test process if --single-process is used.

IN_PROC_BROWSER_TEST_F(NotificationBitmapFetcherBrowserTest,
                       MAYBE_StartTest) {
  GURL url("http://example.com/this-should-work");

  // Put some realistic looking bitmap data into the url_fetcher.
  SkBitmap image;

  // Put a real bitmap into "image".  2x2 bitmap of green 32 bit pixels.
  image.setConfig(SkBitmap::kARGB_8888_Config, 2, 2);
  image.allocPixels();
  image.eraseColor(SK_ColorGREEN);

  // Encode the bits as a PNG.
  std::vector<unsigned char> compressed;
  ASSERT_TRUE(gfx::PNGCodec::EncodeBGRASkBitmap(image, true, &compressed));

  // Copy the bits into the string, and put them into the FakeURLFetcher.
  std::string image_string(compressed.begin(), compressed.end());

  // Set up a delegate to wait for the callback.
  NotificationBitmapFetcherTestDelegate delegate(kAsyncCall);

  NotificationBitmapFetcher fetcher(url, &delegate);

  url_fetcher_factory_->SetFakeResponse(url, image_string, net::HTTP_OK,
                                        net::URLRequestStatus::SUCCESS);

  // We expect that the image decoder will get called and return
  // an image in a callback to OnImageDecoded().
  fetcher.Start(browser()->profile());

  // Blocks until test delegate is notified via a callback.
  content::RunMessageLoop();

  ASSERT_TRUE(delegate.success());

  // Make sure we get back the bitmap we expect.
  const SkBitmap& found_image = delegate.bitmap();
  EXPECT_TRUE(gfx::BitmapsAreEqual(image, found_image));
}

IN_PROC_BROWSER_TEST_F(NotificationBitmapFetcherBrowserTest,
                       OnImageDecodedTest) {
  GURL url("http://example.com/this-should-work-as-well");
  SkBitmap image;

  // Put a real bitmap into "image".  2x2 bitmap of green 16 bit pixels.
  image.setConfig(SkBitmap::kARGB_8888_Config, 2, 2);
  image.allocPixels();
  image.eraseColor(SK_ColorGREEN);

  NotificationBitmapFetcherTestDelegate delegate(kSyncCall);

  NotificationBitmapFetcher fetcher(url, &delegate);

  fetcher.OnImageDecoded(NULL, image);

  // Ensure image is marked as succeeded.
  EXPECT_TRUE(delegate.success());

  // Test that the image is what we expect.
  EXPECT_TRUE(gfx::BitmapsAreEqual(image, delegate.bitmap()));
}

IN_PROC_BROWSER_TEST_F(NotificationBitmapFetcherBrowserTest,
                       OnURLFetchFailureTest) {
  GURL url("http://example.com/this-should-be-fetch-failure");

  // We intentionally put no data into the bitmap to simulate a failure.

  // Set up a delegate to wait for the callback.
  NotificationBitmapFetcherTestDelegate delegate(kAsyncCall);

  NotificationBitmapFetcher fetcher(url, &delegate);

  url_fetcher_factory_->SetFakeResponse(url,
                                        std::string(),
                                        net::HTTP_INTERNAL_SERVER_ERROR,
                                        net::URLRequestStatus::FAILED);

  fetcher.Start(browser()->profile());

  // Blocks until test delegate is notified via a callback.
  content::RunMessageLoop();

  EXPECT_FALSE(delegate.success());
}

IN_PROC_BROWSER_TEST_F(NotificationBitmapFetcherBrowserTest,
                       HandleImageFailedTest) {
  GURL url("http://example.com/this-should-be-a-decode-failure");
  NotificationBitmapFetcherTestDelegate delegate(kAsyncCall);
  NotificationBitmapFetcher fetcher(url, &delegate);
  url_fetcher_factory_->SetFakeResponse(
      url, std::string("Not a real bitmap"),
      net::HTTP_OK, net::URLRequestStatus::SUCCESS);

  fetcher.Start(browser()->profile());

  // Blocks until test delegate is notified via a callback.
  content::RunMessageLoop();

  EXPECT_FALSE(delegate.success());
}

}  // namespace notifier
<commit_msg>Disabled flaky NotificationBitmapFetcherBrowserTest.HandleImageFailedTest on Windows Debug.<commit_after>// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/notifications/sync_notifier/notification_bitmap_fetcher.h"

#include "base/compiler_specific.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/test/test_utils.h"
#include "net/http/http_status_code.h"
#include "net/url_request/test_url_fetcher_factory.h"
#include "net/url_request/url_fetcher.h"
#include "net/url_request/url_request_status.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/gfx/codec/png_codec.h"
#include "ui/gfx/size.h"
#include "ui/gfx/skia_util.h"

namespace {
const bool kAsyncCall = true;
const bool kSyncCall = false;
}  // namespace

namespace notifier {

// Class to catch events from the NotificationBitmapFetcher for testing.
class NotificationBitmapFetcherTestDelegate
    : public NotificationBitmapFetcherDelegate {
 public:
  explicit NotificationBitmapFetcherTestDelegate(bool async)
      : called_(false), success_(false), async_(async) {}

  virtual ~NotificationBitmapFetcherTestDelegate() {
    EXPECT_TRUE(called_);
  }

  // Method inherited from NotificationBitmapFetcherDelegate.
  virtual void OnFetchComplete(const GURL url,
                               const SkBitmap* bitmap) OVERRIDE {
    called_ = true;
    url_ = url;
    if (NULL != bitmap) {
      success_ = true;
      bitmap->deepCopyTo(&bitmap_, bitmap->getConfig());
    }
    // For async calls, we need to quit the message loop so the test can
    // continue.
    if (async_) {
      base::MessageLoop::current()->Quit();
    }
  }

  GURL url() const { return url_; }
  bool success() const { return success_; }
  const SkBitmap& bitmap() const { return bitmap_; }

 private:
  bool called_;
  GURL url_;
  bool success_;
  bool async_;
  SkBitmap bitmap_;

  DISALLOW_COPY_AND_ASSIGN(NotificationBitmapFetcherTestDelegate);
};

class NotificationBitmapFetcherBrowserTest : public InProcessBrowserTest {
 public:
  virtual void SetUp() OVERRIDE {
    url_fetcher_factory_.reset(new net::FakeURLFetcherFactory(NULL));
    InProcessBrowserTest::SetUp();
  }

 protected:
  scoped_ptr<net::FakeURLFetcherFactory> url_fetcher_factory_;
};

#if defined(OS_WIN)
#define MAYBE_StartTest DISABLED_StartTest
#else
#define MAYBE_StartTest StartTest
#endif

// WARNING:  These tests work with --single_process, but not
// --single-process.  The reason is that the sandbox does not get created
// for us by the test process if --single-process is used.

IN_PROC_BROWSER_TEST_F(NotificationBitmapFetcherBrowserTest,
                       MAYBE_StartTest) {
  GURL url("http://example.com/this-should-work");

  // Put some realistic looking bitmap data into the url_fetcher.
  SkBitmap image;

  // Put a real bitmap into "image".  2x2 bitmap of green 32 bit pixels.
  image.setConfig(SkBitmap::kARGB_8888_Config, 2, 2);
  image.allocPixels();
  image.eraseColor(SK_ColorGREEN);

  // Encode the bits as a PNG.
  std::vector<unsigned char> compressed;
  ASSERT_TRUE(gfx::PNGCodec::EncodeBGRASkBitmap(image, true, &compressed));

  // Copy the bits into the string, and put them into the FakeURLFetcher.
  std::string image_string(compressed.begin(), compressed.end());

  // Set up a delegate to wait for the callback.
  NotificationBitmapFetcherTestDelegate delegate(kAsyncCall);

  NotificationBitmapFetcher fetcher(url, &delegate);

  url_fetcher_factory_->SetFakeResponse(url, image_string, net::HTTP_OK,
                                        net::URLRequestStatus::SUCCESS);

  // We expect that the image decoder will get called and return
  // an image in a callback to OnImageDecoded().
  fetcher.Start(browser()->profile());

  // Blocks until test delegate is notified via a callback.
  content::RunMessageLoop();

  ASSERT_TRUE(delegate.success());

  // Make sure we get back the bitmap we expect.
  const SkBitmap& found_image = delegate.bitmap();
  EXPECT_TRUE(gfx::BitmapsAreEqual(image, found_image));
}

IN_PROC_BROWSER_TEST_F(NotificationBitmapFetcherBrowserTest,
                       OnImageDecodedTest) {
  GURL url("http://example.com/this-should-work-as-well");
  SkBitmap image;

  // Put a real bitmap into "image".  2x2 bitmap of green 16 bit pixels.
  image.setConfig(SkBitmap::kARGB_8888_Config, 2, 2);
  image.allocPixels();
  image.eraseColor(SK_ColorGREEN);

  NotificationBitmapFetcherTestDelegate delegate(kSyncCall);

  NotificationBitmapFetcher fetcher(url, &delegate);

  fetcher.OnImageDecoded(NULL, image);

  // Ensure image is marked as succeeded.
  EXPECT_TRUE(delegate.success());

  // Test that the image is what we expect.
  EXPECT_TRUE(gfx::BitmapsAreEqual(image, delegate.bitmap()));
}

IN_PROC_BROWSER_TEST_F(NotificationBitmapFetcherBrowserTest,
                       OnURLFetchFailureTest) {
  GURL url("http://example.com/this-should-be-fetch-failure");

  // We intentionally put no data into the bitmap to simulate a failure.

  // Set up a delegate to wait for the callback.
  NotificationBitmapFetcherTestDelegate delegate(kAsyncCall);

  NotificationBitmapFetcher fetcher(url, &delegate);

  url_fetcher_factory_->SetFakeResponse(url,
                                        std::string(),
                                        net::HTTP_INTERNAL_SERVER_ERROR,
                                        net::URLRequestStatus::FAILED);

  fetcher.Start(browser()->profile());

  // Blocks until test delegate is notified via a callback.
  content::RunMessageLoop();

  EXPECT_FALSE(delegate.success());
}

// Flaky on Win XP Debug: crbug.com/316488
#if defined(OS_WIN) && !defined(NDEBUG)
#define MAYBE_HandleImageFailedTest DISABLED_HandleImageFailedTest
#else
#define MAYBE_HandleImageFailedTest HandleImageFailedTest
#endif

IN_PROC_BROWSER_TEST_F(NotificationBitmapFetcherBrowserTest,
                       MAYBE_HandleImageFailedTest) {
  GURL url("http://example.com/this-should-be-a-decode-failure");
  NotificationBitmapFetcherTestDelegate delegate(kAsyncCall);
  NotificationBitmapFetcher fetcher(url, &delegate);
  url_fetcher_factory_->SetFakeResponse(
      url, std::string("Not a real bitmap"),
      net::HTTP_OK, net::URLRequestStatus::SUCCESS);

  fetcher.Start(browser()->profile());

  // Blocks until test delegate is notified via a callback.
  content::RunMessageLoop();

  EXPECT_FALSE(delegate.success());
}

}  // namespace notifier
<|endoftext|>