<commit_before>// Copyright (c) 2014, Robert Escriva
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright notice,
//       this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//     * Neither the name of Replicant nor the names of its contributors may be
//       used to endorse or promote products derived from this software without
//       specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

// C
#include <cmath>

// BusyBee
#include "busybee_constants.h"

// Replicant
#include "common/network_msgtype.h"
#include "daemon/failure_detector.h"
#include "daemon/failure_manager.h"

using replicant::failure_detector;
using replicant::failure_manager;

failure_manager :: failure_manager()
    : m_fds()
{
}

failure_manager :: ~failure_manager() throw ()
{
}

void
failure_manager :: track(uint64_t now, const std::vector<uint64_t>& tokens,
                         uint64_t interval, uint64_t window_sz)
{
    for (size_t i = 1; i < tokens.size(); ++i)
    {
        assert(tokens[i - 1] < tokens[i]);
    }

    size_t old_idx = 0;
    size_t new_idx = 0;
    fd_vector_t replacement;
    replacement.reserve(tokens.size());

    while (old_idx < m_fds.size() && new_idx < tokens.size())
    {
        if (m_fds[old_idx].first == tokens[new_idx])
        {
            replacement.push_back(m_fds[old_idx]);
            ++old_idx;
            ++new_idx;
        }
        else if (m_fds[old_idx].first < tokens[new_idx])
        {
            ++old_idx;
        }
        else if (m_fds[old_idx].first > tokens[new_idx])
        {
            fd_ptr fd = new failure_detector(now, interval, window_sz);
            replacement.push_back(std::make_pair(tokens[new_idx], fd));
            ++new_idx;
        }
    }

    while (new_idx < tokens.size())
    {
        fd_ptr fd = new failure_detector(now, interval, window_sz);
        replacement.push_back(std::make_pair(tokens[new_idx], fd));
        ++new_idx;
    }

    m_fds.swap(replacement);
}

void
failure_manager :: ping(daemon* d,
                        bool (daemon::*s)(uint64_t, std::auto_ptr<e::buffer>),
                        uint64_t version)
{
    for (size_t i = 0; i < m_fds.size(); ++i)
    {
        uint64_t seqno = m_fds[i].second->seqno();
        size_t sz = BUSYBEE_HEADER_SIZE
                  + pack_size(REPLNET_PING)
                  + sizeof(uint64_t)
                  + sizeof(uint64_t);
        std::auto_ptr<e::buffer> msg(e::buffer::create(sz));
        msg->pack_at(BUSYBEE_HEADER_SIZE) << REPLNET_PING << version << seqno;
        (*d.*s)(m_fds[i].first, msg);
    }
}

void
failure_manager :: pong(uint64_t token, uint64_t seqno, uint64_t now)
{
    failure_detector* fd = find(token);

    if (!fd)
    {
        return;
    }

    fd->heartbeat(seqno, now);
}

double
failure_manager :: suspicion(uint64_t token, uint64_t now) const
{
    failure_detector* fd = find(token);

    if (!fd)
    {
        return -1;
    }

    return fd->suspicion(now);
}

namespace
{

struct suspicion_stats
{
    uint64_t token;
    double suspicion;
    double mean;
    double stdev;
};

bool
compare_suspicions(const suspicion_stats& lhs, const suspicion_stats& rhs)
{
    return lhs.suspicion < rhs.suspicion;
}

}

void
failure_manager :: get_suspicions(uint64_t now, std::vector<uint64_t>* tokens, size_t* cutoff)
{
    std::vector<suspicion_stats> stats;
    stats.reserve(tokens->size());

    for (size_t i = 0; i < tokens->size(); ++i)
    {
        double d = suspicion((*tokens)[i], now);

        if (d < 0)
        {
            d = HUGE_VAL;
        }

        stats.push_back(suspicion_stats());
        stats.back().token = (*tokens)[i];
        stats.back().suspicion = d;
    }

    std::sort(stats.begin(), stats.end(), compare_suspicions);
    double n = 0;
    double mean = 0;
    double M2 = 0;

    for (size_t i = 0; i < stats.size(); ++i)
    {
        if (std::isinf(stats[i].suspicion))
        {
            break;
        }

        ++n;
        double delta = stats[i].suspicion - mean;
        mean = mean + delta / n;
        M2 = M2 + delta * (stats[i].suspicion - mean);
        stats[i].mean = mean;
        stats[i].stdev = n > 1 ? sqrt(M2 / (n - 1)) : 0;
    }

    for (size_t i = 0; i < stats.size(); ++i)
    {
        (*tokens)[i] = stats[i].token;
    }

    for (size_t i = 0; i < stats.size(); ++i)
    {
        if (std::isinf(stats[*cutoff].suspicion))
        {
            *cutoff = i;
            return;
        }
    }

    if (stats.empty())
    {
        *cutoff = 0;
        return;
    }

    for (size_t i = stats.size() - 1; i > 0; --i)
    {
        double threshold = stats[*cutoff - 1].mean
                         + 6 * std::max(stats[*cutoff - 1].stdev, 0.25);

        if (stats[*cutoff].suspicion > threshold)
        {
            *cutoff = i;
            return;
        }
    }

    if (stats[0].suspicion > 10.)
    {
        *cutoff = 0;
        return;
    }

    *cutoff = stats.size();
}

failure_detector*
failure_manager :: find(uint64_t token) const
{
    fd_vector_t::const_iterator it = std::lower_bound(m_fds.begin(), m_fds.end(),
                                                      std::make_pair(token, fd_ptr()));

    if (it == m_fds.end() || it->first != token)
    {
        return NULL;
    }

    return it->second.get();
}
<commit_msg>Fix failure_manager.<commit_after>// Copyright (c) 2014, Robert Escriva
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright notice,
//       this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//     * Neither the name of Replicant nor the names of its contributors may be
//       used to endorse or promote products derived from this software without
//       specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

// C
#include <cmath>

// BusyBee
#include "busybee_constants.h"

// Replicant
#include "common/network_msgtype.h"
#include "daemon/failure_detector.h"
#include "daemon/failure_manager.h"

using replicant::failure_detector;
using replicant::failure_manager;

failure_manager :: failure_manager()
    : m_fds()
{
}

failure_manager :: ~failure_manager() throw ()
{
}

void
failure_manager :: track(uint64_t now, const std::vector<uint64_t>& tokens,
                         uint64_t interval, uint64_t window_sz)
{
    for (size_t i = 1; i < tokens.size(); ++i)
    {
        assert(tokens[i - 1] < tokens[i]);
    }

    size_t old_idx = 0;
    size_t new_idx = 0;
    fd_vector_t replacement;
    replacement.reserve(tokens.size());

    while (old_idx < m_fds.size() && new_idx < tokens.size())
    {
        if (m_fds[old_idx].first == tokens[new_idx])
        {
            replacement.push_back(m_fds[old_idx]);
            ++old_idx;
            ++new_idx;
        }
        else if (m_fds[old_idx].first < tokens[new_idx])
        {
            ++old_idx;
        }
        else if (m_fds[old_idx].first > tokens[new_idx])
        {
            fd_ptr fd = new failure_detector(now, interval, window_sz);
            replacement.push_back(std::make_pair(tokens[new_idx], fd));
            ++new_idx;
        }
    }

    while (new_idx < tokens.size())
    {
        fd_ptr fd = new failure_detector(now, interval, window_sz);
        replacement.push_back(std::make_pair(tokens[new_idx], fd));
        ++new_idx;
    }

    m_fds.swap(replacement);
}

void
failure_manager :: ping(daemon* d,
                        bool (daemon::*s)(uint64_t, std::auto_ptr<e::buffer>),
                        uint64_t version)
{
    for (size_t i = 0; i < m_fds.size(); ++i)
    {
        uint64_t seqno = m_fds[i].second->seqno();
        size_t sz = BUSYBEE_HEADER_SIZE
                  + pack_size(REPLNET_PING)
                  + sizeof(uint64_t)
                  + sizeof(uint64_t);
        std::auto_ptr<e::buffer> msg(e::buffer::create(sz));
        msg->pack_at(BUSYBEE_HEADER_SIZE) << REPLNET_PING << version << seqno;
        (*d.*s)(m_fds[i].first, msg);
    }
}

void
failure_manager :: pong(uint64_t token, uint64_t seqno, uint64_t now)
{
    failure_detector* fd = find(token);

    if (!fd)
    {
        return;
    }

    fd->heartbeat(seqno, now);
}

double
failure_manager :: suspicion(uint64_t token, uint64_t now) const
{
    failure_detector* fd = find(token);

    if (!fd)
    {
        return -1;
    }

    return fd->suspicion(now);
}

namespace
{

struct suspicion_stats
{
    uint64_t token;
    double suspicion;
    double mean;
    double stdev;
};

bool
compare_suspicions(const suspicion_stats& lhs, const suspicion_stats& rhs)
{
    return lhs.suspicion < rhs.suspicion;
}

}

void
failure_manager :: get_suspicions(uint64_t now, std::vector<uint64_t>* tokens, size_t* cutoff)
{
    std::vector<suspicion_stats> stats;
    stats.reserve(tokens->size());

    for (size_t i = 0; i < tokens->size(); ++i)
    {
        double d = suspicion((*tokens)[i], now);

        if (d < 0)
        {
            d = HUGE_VAL;
        }

        stats.push_back(suspicion_stats());
        stats.back().token = (*tokens)[i];
        stats.back().suspicion = d;
    }

    std::sort(stats.begin(), stats.end(), compare_suspicions);
    double n = 0;
    double mean = 0;
    double M2 = 0;

    for (size_t i = 0; i < stats.size(); ++i)
    {
        if (std::isinf(stats[i].suspicion))
        {
            break;
        }

        ++n;
        double delta = stats[i].suspicion - mean;
        mean = mean + delta / n;
        M2 = M2 + delta * (stats[i].suspicion - mean);
        stats[i].mean = mean;
        stats[i].stdev = n > 1 ? sqrt(M2 / (n - 1)) : 0;
    }

    for (size_t i = 0; i < stats.size(); ++i)
    {
        (*tokens)[i] = stats[i].token;
    }

    for (size_t i = 0; i < stats.size(); ++i)
    {
        if (std::isinf(stats[i].suspicion))
        {
            *cutoff = i;
            return;
        }
    }

    if (stats.empty())
    {
        *cutoff = 0;
        return;
    }

    for (size_t i = stats.size() - 1; i > 0; --i)
    {
        double threshold = stats[i - 1].mean
                         + 6 * std::max(stats[i - 1].stdev, 0.25);

        if (stats[i].suspicion > threshold)
        {
            *cutoff = i;
            return;
        }
    }

    if (stats[0].suspicion > 10.)
    {
        *cutoff = 0;
        return;
    }

    *cutoff = stats.size();
}

failure_detector*
failure_manager :: find(uint64_t token) const
{
    fd_vector_t::const_iterator it = std::lower_bound(m_fds.begin(), m_fds.end(),
                                                      std::make_pair(token, fd_ptr()));

    if (it == m_fds.end() || it->first != token)
    {
        return NULL;
    }

    return it->second.get();
}
<|endoftext|>


<commit_before>///
/// @file  MpiLoadBalancer.cpp
/// @brief The MpiLoadBalancer evenly distributes the computation
///        of the hard special leaves onto cluster nodes.
///
///        Simply parallelizing the computation of the special
///        leaves in the Lagarias-Miller-Odlyzko algorithm by
///        subdividing the sieve interval by the number of threads
///        into equally sized subintervals does not scale because
///        the distribution of the special leaves is highly skewed
///        and most special leaves are in the first few segments
///        whereas later on there are very few special leaves.
///
///        This MpiLoadBalancer gradually increases the number of
///        segments to sieve as long the expected runtime of the
///        sieve distance is smaller than the expected finish time
///        of the algorithm. Near the end the MpiLoadBalancer will
///        gradually decrease the number of segments to sieve in
///        order to prevent that 1 thread will run much longer
///        than all the other threads.
///
/// Copyright (C) 2017 Kim Walisch, <kim.walisch@gmail.com>
///
/// This file is distributed under the BSD License. See the COPYING
/// file in the top level directory.
///

#include <primecount-internal.hpp>
#include <MpiLoadBalancer.hpp>
#include <MpiMsg.hpp>
#include <imath.hpp>
#include <Sieve.hpp>

#include <stdint.h>
#include <algorithm>

using namespace std;

namespace primecount {

MpiLoadBalancer::MpiLoadBalancer(maxint_t x,
                                 int64_t y,
                                 int64_t z,
                                 maxint_t s2_approx) :
  z_(z),
  limit_(z + 1),
  low_(0),
  max_low_(0),
  segments_(1),
  s2_hard_(0),
  s2_approx_(s2_approx),
  time_(get_wtime()),
  status_(x)
{
  int64_t sqrtz = isqrt(z);
  segment_size_ = Sieve::get_segment_size(sqrtz);

  double alpha = get_alpha(x, y);
  maxint_t x16 = iroot<6>(x);
  smallest_hard_leaf_ = (int64_t) (x / (y * sqrt(alpha) * x16));
}

void MpiLoadBalancer::get_work(MpiMsg* msg,
                               maxint_t s2_hard)
{
  s2_hard_ += msg->s2_hard<maxint_t>();

  if (msg->low() > max_low_)
  {
    max_low_ = msg->low();
    segments_ = msg->segments();
    segment_size_ = msg->segment_size();

    Runtime runtime;
    runtime.init = msg->init_seconds();
    runtime.secs = msg->seconds();

    double next = get_next(runtime);
    next = in_between(0.25, next, 2.0);
    next *= segments_;
    next = max(1.0, next);
    segments_ = (int64_t) next;
  }

  auto high = low_ + segments_ * segment_size_;

  // Most hard special leaves are located just past
  // smallest_hard_leaf_. In order to prevent assigning
  // the bulk of work to a single thread we reduce
  // the number of segments to a minimum.
  //
  if (smallest_hard_leaf_ >= low_ &&
      smallest_hard_leaf_ <= high)
  {
    segments_ = 1;
  }

  // udpate msg with new work todo
  msg->update(low_, segments_, segment_size_);

  low_ += segments_ * segment_size_;
  low_ = min(low_, limit_);
}

double MpiLoadBalancer::get_next(Runtime& runtime) const
{
  double min_secs = runtime.init * 10;
  double run_secs = runtime.secs;

  min_secs = max(min_secs, 0.01);
  run_secs = max(run_secs, min_secs / 10);

  double rem = remaining_secs();
  double threshold = rem / 4;
  threshold = max(threshold, min_secs);

  return threshold / run_secs;
}

/// Remaining seconds till finished
double MpiLoadBalancer::remaining_secs() const
{
  double percent = status_.getPercent(low_, z_, s2_hard_, s2_approx_);
  percent = in_between(20, percent, 100);

  double total_secs = get_wtime() - time_;
  double secs = total_secs * (100 / percent) - total_secs;
  return secs;
}

} // namespace
<commit_msg>Improve cache efficiency<commit_after>///
/// @file  MpiLoadBalancer.cpp
/// @brief The MpiLoadBalancer evenly distributes the computation
///        of the hard special leaves onto cluster nodes.
///
///        Simply parallelizing the computation of the special
///        leaves in the Lagarias-Miller-Odlyzko algorithm by
///        subdividing the sieve interval by the number of threads
///        into equally sized subintervals does not scale because
///        the distribution of the special leaves is highly skewed
///        and most special leaves are in the first few segments
///        whereas later on there are very few special leaves.
///
///        This MpiLoadBalancer gradually increases the number of
///        segments to sieve as long the expected runtime of the
///        sieve distance is smaller than the expected finish time
///        of the algorithm. Near the end the MpiLoadBalancer will
///        gradually decrease the number of segments to sieve in
///        order to prevent that 1 thread will run much longer
///        than all the other threads.
///
/// Copyright (C) 2017 Kim Walisch, <kim.walisch@gmail.com>
///
/// This file is distributed under the BSD License. See the COPYING
/// file in the top level directory.
///

#include <primecount-internal.hpp>
#include <MpiLoadBalancer.hpp>
#include <MpiMsg.hpp>
#include <imath.hpp>
#include <Sieve.hpp>

#include <stdint.h>
#include <algorithm>

using namespace std;

namespace primecount {

MpiLoadBalancer::MpiLoadBalancer(maxint_t x,
                                 int64_t y,
                                 int64_t z,
                                 maxint_t s2_approx) :
  z_(z),
  limit_(z + 1),
  low_(0),
  max_low_(0),
  segments_(1),
  s2_hard_(0),
  s2_approx_(s2_approx),
  time_(get_wtime()),
  status_(x)
{
  double alpha = get_alpha(x, y);
  maxint_t x16 = iroot<6>(x);
  smallest_hard_leaf_ = (int64_t) (x / (y * sqrt(alpha) * x16));

  // try to use a segment size that fits exactly
  // into the CPUs L1 data cache
  int64_t l1_dcache_size = 1 << 15;
  int64_t size = l1_dcache_size * 30;
  size = max(size, isqrt(z));
  segment_size_ = Sieve::get_segment_size(size);
}

void MpiLoadBalancer::get_work(MpiMsg* msg, maxint_t s2_hard)
{
  s2_hard_ += msg->s2_hard<maxint_t>();

  if (msg->low() > max_low_)
  {
    max_low_ = msg->low();
    segments_ = msg->segments();
    segment_size_ = msg->segment_size();

    Runtime runtime;
    runtime.init = msg->init_seconds();
    runtime.secs = msg->seconds();

    double next = get_next(runtime);
    next = in_between(0.25, next, 2.0);
    next *= segments_;
    next = max(1.0, next);
    segments_ = (int64_t) next;
  }

  auto high = low_ + segments_ * segment_size_;

  // Most hard special leaves are located just past
  // smallest_hard_leaf_. In order to prevent assigning
  // the bulk of work to a single thread we reduce
  // the number of segments to a minimum.
  //
  if (smallest_hard_leaf_ >= low_ &&
      smallest_hard_leaf_ <= high)
  {
    segments_ = 1;
  }

  // udpate msg with new work todo
  msg->update(low_, segments_, segment_size_);

  low_ += segments_ * segment_size_;
  low_ = min(low_, limit_);
}

double MpiLoadBalancer::get_next(Runtime& runtime) const
{
  double min_secs = runtime.init * 10;
  double run_secs = runtime.secs;

  min_secs = max(min_secs, 0.01);
  run_secs = max(run_secs, min_secs / 10);

  double rem = remaining_secs();
  double threshold = rem / 4;
  threshold = max(threshold, min_secs);

  return threshold / run_secs;
}

/// Remaining seconds till finished
double MpiLoadBalancer::remaining_secs() const
{
  double percent = status_.getPercent(low_, z_, s2_hard_, s2_approx_);
  percent = in_between(20, percent, 100);

  double total_secs = get_wtime() - time_;
  double secs = total_secs * (100 / percent) - total_secs;
  return secs;
}

} // namespace
<|endoftext|>


<commit_before>#include <iostream>
#include <fstream>
#include <sstream>
#include <cmath>
#include "lpmConfig.h"
#include "OutputMessage.h"
#include "Logger.h"
#include "GlobalConstants.h"
#include "LpmXyzVector.hpp"
#include "LpmEuclideanCoords.hpp"
#include "LpmSphereCoords.hpp"
#include "LpmParticles.hpp"
#include "LpmScalarField.hpp"
#include "LpmField2d.hpp"
#include "LpmField3d.hpp"

typedef double ST;
using LpmXyzVector::XyzVector;

ST sphHarm54( const ST lat, const ST lon) {
	static const ST amp = (3.0 / 16.0 ) * std::sqrt(385.0 / (2.0 * GlobalConstants::Instance()->Pi()));
	return amp * std::cos( 4.0 * lon ) * std::pow( std::sin(lat), 4) * std::cos(lat);
}

int main ( int argc, const char* argv[] ) {
	Logger* log = Logger::Instance(OutputMessage::debugPriority);

	std::stringstream ss;
    std::string s;
    ss << "-- LPM Version " << LPM_VERSION_MAJOR << "." << LPM_VERSION_MINOR << " -- \n ";
    ss << "   Unit Test " << argv[0] << ": covers LpmParticles.hpp, LpmScalarField.hpp, "
       << "LpmField2d.hpp and LpmField3d.hpp.";
    OutputMessage introMsg( ss.str(), OutputMessage::remarkPriority, "main");
    log->logMessage(introMsg);

    /*
    TEST 1 : Planar particles, Cartesian geometry
    */
    ss.str(s);
    ss << "TEST 1: Particles and Fields in the plane" ;
    OutputMessage test1Start( ss.str(), OutputMessage::remarkPriority, "main");
    log->logMessage(test1Start);

    const int nn = 101;
    const ST dx = 0.1;

    LpmParticles<ST> planeParticles( LpmParticles<ST>::PlanarCartesian, nn*nn );
    planeParticles.PrintStats( "main, after construction.");
    for ( int i = 0; i < nn; ++i ) {
    	const ST xi = -5.0 + i * dx;
    	for ( int j = 0; j < nn/2; ++j ) {
    		const ST yj = -5.0 + j * dx;
    		planeParticles.insert( xi, yj );
    	}
    	for ( int j = nn/2; j < nn; ++j ) {
			const ST yj = -5.0 + j * dx;
			const XyzVector<ST> physX(xi,yj);
			const XyzVector<ST> lagX(physX);
			planeParticles.insert( physX, lagX );
    	}
    }
    planeParticles.PrintStats( "main, after particle insertion.");

    std::string matlabFile = "planarParticlesUnitTest.m";
    std::ofstream file( matlabFile.c_str() );
    if ( !file ) {
    	OutputMessage fileErrMsg("ERROR: cannot open planar output file.", OutputMessage::errorPriority,
    		 "lpmParticlesAndFieldTest.cpp::main");
    	log->logMessage(fileErrMsg);
    	return 1;
    }

    planeParticles.writePhysCoordsToMatlab( file );
    planeParticles.writeLagCoordsToMatlab( file );
    planeParticles.writeAreaToMatlab( file );

    LpmScalarField<ST> gaussHill( planeParticles, "gaussHill", "n_a" );
    for ( int i = 0; i < planeParticles.size(); ++i ) {
    	const XyzVector<ST> physX = planeParticles.physCoordVec(i);
    	gaussHill.insert( std::exp( -physX.x * physX.x - physX.y * physX.y ) );
    }
    gaussHill.PrintStats( "main, after field value insertion");
    gaussHill.writeFieldToMatlab( file );

    LpmField2d<ST> rotatingField( nn * nn, "rotation", "n_a" );
    for ( int i = 0; i < planeParticles.size() / 2; ++i ) {
    	const XyzVector<ST> physX = planeParticles.physCoordVec(i);
    	rotatingField.insert( - physX.y, physX.x );
    }
    for ( int i = planeParticles.size()/2; i < planeParticles.size(); ++i ) {
    	const XyzVector<ST> physX = planeParticles.physCoordVec(i);
    	const XyzVector<ST> fieldVec( - physX.y, physX.x );
    	rotatingField.insert( fieldVec );
    }
    rotatingField.PrintStats("main, after field2d value insertion.");
    rotatingField.writeFieldToMatlab( file );

    OutputMessage endTestMsg("TEST COMPLETE", OutputMessage::remarkPriority, "main");
    log->logMessage(endTestMsg);

    /*
    TEST 2 : 3d particles, Euclidean geometry
    */
	OutputMessage test2start("TEST 2: 3D Particles and Fields", OutputMessage::remarkPriority, "main");
	log->logMessage(test2start);

	log->logMessage(endTestMsg);

	/*
	TEST 3 : Spherical surface
	*/
	OutputMessage test3start("TEST 3: Spherical surface particles and fields", OutputMessage::remarkPriority, "main");
	log->logMessage(test3start);

	const ST PI = GlobalConstants::Instance()->Pi();
	const ST deg2rad = GlobalConstants::Instance()->Deg2Rad();
	const int nLat = 91;
	const int nLon = 180;
	const ST dLam = 360.0/nLon * deg2rad;

	LpmParticles<ST> sphereParticles( LpmParticles<ST>::SphericalSurface, nLon * nLat );
	for ( int j = 0; j < nLon; ++j ) {
		const ST lon = j * dLam;
		for ( int i = 0; i < nLat; ++i ) {
			const ST lat = -0.5 * PI + i * dLam;
			const XyzVector<ST> sphX( std::cos(lon) * std::cos(lat), std::sin(lon) * std::cos(lat), std::sin(lat));

			sphereParticles.insert( sphX, sphX );
		}
	}
	sphereParticles.PrintStats("main, after sphere insertion");
	sphereParticles.writePhysCoordsToMatlab( file );

	LpmScalarField<ST> rhWave( sphereParticles, "rhWave", "n_a");
	for ( int i = 0; i < sphereParticles.size(); ++i ) {
		rhWave.insert( sphHarm54( sphereParticles.Latitude(i), sphereParticles.Longitude(i) ) );
	}
	rhWave.writeFieldToMatlab(file);
    log->logMessage(endTestMsg);
return 0;
};
<commit_msg>updated particles test to include distance function.<commit_after>#include <iostream>
#include <fstream>
#include <sstream>
#include <cmath>
#include "lpmConfig.h"
#include "OutputMessage.h"
#include "Logger.h"
#include "GlobalConstants.h"
#include "LpmXyzVector.hpp"
#include "LpmEuclideanCoords.hpp"
#include "LpmSphereCoords.hpp"
#include "LpmParticles.hpp"
#include "LpmScalarField.hpp"
#include "LpmField2d.hpp"
#include "LpmField3d.hpp"

typedef double ST;
using LpmXyzVector::XyzVector;

ST sphHarm54( const ST lat, const ST lon) {
	static const ST amp = (3.0 / 16.0 ) * std::sqrt(385.0 / (2.0 * GlobalConstants::Instance()->Pi()));
	return amp * std::cos( 4.0 * lon ) * std::pow( std::sin(lat), 4) * std::cos(lat);
}

int main ( int argc, const char* argv[] ) {
	Logger* log = Logger::Instance(OutputMessage::debugPriority);

	std::stringstream ss;
    std::string s;
    ss << "-- LPM Version " << LPM_VERSION_MAJOR << "." << LPM_VERSION_MINOR << " -- \n ";
    ss << "   Unit Test " << argv[0] << ": covers LpmParticles.hpp, LpmScalarField.hpp, "
       << "LpmField2d.hpp and LpmField3d.hpp.";
    OutputMessage introMsg( ss.str(), OutputMessage::remarkPriority, "main");
    log->logMessage(introMsg);

    /*
    TEST 1 : Planar particles, Cartesian geometry
    */
    ss.str(s);
    ss << "TEST 1: Particles and Fields in the plane" ;
    OutputMessage test1Start( ss.str(), OutputMessage::remarkPriority, "main");
    log->logMessage(test1Start);

    const int nn = 101;
    const ST dx = 0.1;

    LpmParticles<ST> planeParticles( LpmParticles<ST>::PlanarCartesian, nn*nn );
    planeParticles.PrintStats( "main, after construction.");
    for ( int i = 0; i < nn; ++i ) {
    	const ST xi = -5.0 + i * dx;
    	for ( int j = 0; j < nn/2; ++j ) {
    		const ST yj = -5.0 + j * dx;
    		planeParticles.insert( xi, yj );
    	}
    	for ( int j = nn/2; j < nn; ++j ) {
			const ST yj = -5.0 + j * dx;
			const XyzVector<ST> physX(xi,yj);
			const XyzVector<ST> lagX(physX);
			planeParticles.insert( physX, lagX );
    	}
    }
    planeParticles.PrintStats( "main, after particle insertion.");

    ss.str(s);
    ss << "distance between particle 0 and particle " << planeParticles.size() - 1 << " should be " << 10.0 * std::sqrt(2.0) << "; computed distance is " << planeParticles.distance(0, nn * nn - 1 );
    OutputMessage statusMsg(ss.str(), OutputMessage::remarkPriority, "main, plane particles Test 1");
    log->logMessage(statusMsg);

    std::string matlabFile = "planarParticlesUnitTest.m";
    std::ofstream file( matlabFile.c_str() );
    if ( !file ) {
    	OutputMessage fileErrMsg("ERROR: cannot open planar output file.", OutputMessage::errorPriority,
    		 "lpmParticlesAndFieldTest.cpp::main");
    	log->logMessage(fileErrMsg);
    	return 1;
    }

    planeParticles.writePhysCoordsToMatlab( file );
    planeParticles.writeLagCoordsToMatlab( file );
    planeParticles.writeAreaToMatlab( file );

    LpmScalarField<ST> gaussHill( planeParticles, "gaussHill", "n_a" );
    for ( int i = 0; i < planeParticles.size(); ++i ) {
    	const XyzVector<ST> physX = planeParticles.physCoordVec(i);
    	gaussHill.insert( std::exp( -physX.x * physX.x - physX.y * physX.y ) );
    }
    gaussHill.PrintStats( "main, after field value insertion");
    gaussHill.writeFieldToMatlab( file );

    LpmField2d<ST> rotatingField( nn * nn, "rotation", "n_a" );
    for ( int i = 0; i < planeParticles.size() / 2; ++i ) {
    	const XyzVector<ST> physX = planeParticles.physCoordVec(i);
    	rotatingField.insert( - physX.y, physX.x );
    }
    for ( int i = planeParticles.size()/2; i < planeParticles.size(); ++i ) {
    	const XyzVector<ST> physX = planeParticles.physCoordVec(i);
    	const XyzVector<ST> fieldVec( - physX.y, physX.x );
    	rotatingField.insert( fieldVec );
    }
    rotatingField.PrintStats("main, after field2d value insertion.");
    rotatingField.writeFieldToMatlab( file );

    OutputMessage endTestMsg("TEST COMPLETE", OutputMessage::remarkPriority, "main");
    log->logMessage(endTestMsg);

    /*
    TEST 2 : 3d particles, Euclidean geometry
    */
	OutputMessage test2start("TEST 2: 3D Particles and Fields", OutputMessage::remarkPriority, "main");
	log->logMessage(test2start);

	log->logMessage(endTestMsg);

	/*
	TEST 3 : Spherical surface
	*/
	OutputMessage test3start("TEST 3: Spherical surface particles and fields", OutputMessage::remarkPriority, "main");
	log->logMessage(test3start);

	const ST PI = GlobalConstants::Instance()->Pi();
	const ST deg2rad = GlobalConstants::Instance()->Deg2Rad();
	const int nLat = 91;
	const int nLon = 180;
	const ST dLam = 360.0/nLon * deg2rad;

	LpmParticles<ST> sphereParticles( LpmParticles<ST>::SphericalSurface, nLon * nLat );
	for ( int j = 0; j < nLon; ++j ) {
		const ST lon = j * dLam;
		for ( int i = 0; i < nLat; ++i ) {
			const ST lat = -0.5 * PI + i * dLam;
			const XyzVector<ST> sphX( std::cos(lon) * std::cos(lat), std::sin(lon) * std::cos(lat), std::sin(lat));

			sphereParticles.insert( sphX, sphX );
		}
	}
	sphereParticles.PrintStats("main, after sphere insertion");
	sphereParticles.writePhysCoordsToMatlab( file );

	ss.str(s);
	ss << "distance between particle 0 and particle " << sphereParticles.size() - 1 << " should be " << PI << "; computed distance is " << sphereParticles.distance(0, nLon * nLat - 1);
	statusMsg = OutputMessage( ss.str(), OutputMessage::remarkPriority, "main, sphere particles test.");
	log->logMessage(statusMsg);

	LpmScalarField<ST> rhWave( sphereParticles, "rhWave", "n_a");
	for ( int i = 0; i < sphereParticles.size(); ++i ) {
		rhWave.insert( sphHarm54( sphereParticles.Latitude(i), sphereParticles.Longitude(i) ) );
	}
	rhWave.writeFieldToMatlab(file);
    log->logMessage(endTestMsg);
return 0;
};
<|endoftext|>


<commit_before>//
//  openssl_async_echo_client.cc
//
//  clang++ -std=c++11 openssl_async_echo_client.cc -lcrypto -lssl -o openssl_async_echo_client
//
//  * example of non-blocking TLS
//  * tested with openssl/boringssl
//  * probably leaks
//

#include <netdb.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/poll.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <cstdio>
#include <cstdarg>
#include <cstring>
#include <cerrno>
#include <csignal>
#include <vector>
#include <map>
#include <algorithm>

#include <openssl/crypto.h>
#include <openssl/ssl.h>
#include <openssl/err.h>

static const char* connect_host = "127.0.0.1";
static int connect_port = 8443;

static const char* ssl_cacert_file = "ssl/cacert.pem";

enum ssl_state
{
    ssl_none,
    ssl_handshake_read,
    ssl_handshake_write,
    ssl_app_read,
    ssl_app_write
};

static const char* state_names[] =
{
    "ssl_none",
    "ssl_handshake_read",
    "ssl_handshake_write",
    "ssl_app_read",
    "ssl_app_write"
};

struct tls_connection
{
    tls_connection(int fd, SSL *ssl)
        : fd(fd), ssl(ssl), state(ssl_none) {}
    tls_connection(const tls_connection &o)
        : fd(o.fd), ssl(o.ssl), state(o.state) {}

    int fd;
    SSL *ssl;
    ssl_state state;
};

struct tls_echo_client
{
    std::vector<struct pollfd> poll_vec;
    std::map<int,tls_connection> tls_connection_map;

    void update_state(tls_connection &conn, int events, ssl_state new_state);
    void update_state(tls_connection &conn, int ssl_err);
    void close_connection(tls_connection &conn);
    void mainloop();
};

static void log_prefix(const char* prefix, const char* fmt, va_list args)
{
    std::vector<char> buf(256);
    int len = vsnprintf(buf.data(), buf.capacity(), fmt, args);
    if (len >= (int)buf.capacity()) {
        buf.resize(len + 1);
        vsnprintf(buf.data(), buf.capacity(), fmt, args);
    }
    fprintf(stderr, "%s: %s\n", prefix, buf.data());
}

static void log_fatal_exit(const char* fmt, ...)
{
    va_list args;
    va_start(args, fmt);
    log_prefix("fatal", fmt, args);
    va_end(args);
    exit(9);
}

static void log_debug(const char* fmt, ...)
{
    va_list args;
    va_start(args, fmt);
    log_prefix("debug", fmt, args);
    va_end(args);
}

static int log_tls_errors(const char *str, size_t len, void *bio)
{
    fprintf(stderr, "%s", str);
    return 0;
}

void tls_echo_client::update_state(tls_connection &conn, int events, ssl_state new_state)
{
    log_debug("fd=%d %s -> %s",
              conn.fd, state_names[conn.state], state_names[new_state]);
    conn.state = new_state;
    auto pi = std::find_if(poll_vec.begin(), poll_vec.end(),
                           [&] (const struct pollfd &pfd) { return pfd.fd == conn.fd; });
    if (pi != poll_vec.end()) pi->events = events;
    else log_fatal_exit("file descriptor missing from poll_vec: %d", conn.fd);
}

void tls_echo_client::update_state(tls_connection &conn, int ssl_err)
{
    switch (ssl_err) {
        case SSL_ERROR_WANT_READ:
            update_state(conn, POLLIN, ssl_handshake_read);
            break;
        case SSL_ERROR_WANT_WRITE:
            update_state(conn, POLLOUT, ssl_handshake_write);
            break;
        default:
            log_fatal_exit("unknown tls error: %d", ssl_err);
            break;
    }
}

void tls_echo_client::close_connection(tls_connection &conn)
{
    log_debug("connection closed");
    int fd = conn.fd;
    close(fd);
    auto pi = std::find_if(poll_vec.begin(), poll_vec.end(),
                           [fd] (const struct pollfd &pfd) { return pfd.fd == fd; });
    if (pi != poll_vec.end()) poll_vec.erase(pi);
    tls_connection_map.erase(fd);
}

void tls_echo_client::mainloop()
{
    SSL_CTX *ctx = SSL_CTX_new(TLSv1_client_method());

    if ((!SSL_CTX_load_verify_locations(ctx, ssl_cacert_file, NULL)) ||
        (!SSL_CTX_set_default_verify_paths(ctx))) {
        ERR_print_errors_cb(log_tls_errors, NULL);
        log_fatal_exit("failed to load cacert: %s", ssl_cacert_file);
    } else {
        log_debug("loaded cacert: %s", ssl_cacert_file);
    }
    SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
    SSL_CTX_set_verify_depth(ctx, 9);

    sockaddr_in saddr;
    memset(&saddr, 0, sizeof(saddr));
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(connect_port);

    struct hostent *he = gethostbyname(connect_host);
    if (he) {
        if (he->h_addrtype == AF_INET) {
            memcpy(&saddr.sin_addr, he->h_addr_list[0], he->h_length);
        } else {
            log_fatal_exit("unknown address type: %s", he->h_addrtype);
        }
    } else {
        log_fatal_exit("unknown host %s", connect_host);
    }

    int connect_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (connect_fd < 0) {
        log_fatal_exit("socket failed: %s", strerror(errno));
    }
    if (fcntl(connect_fd, F_SETFD, FD_CLOEXEC) < 0) {
        log_fatal_exit("fcntl(F_SETFD, FD_CLOEXEC) failed: %s", strerror(errno));
    }
    if (fcntl(connect_fd, F_SETFL, O_NONBLOCK) < 0) {
        log_fatal_exit("fcntl(F_SETFL, O_NONBLOCK) failed: %s", strerror(errno));
    }
    socklen_t addr_size = sizeof(sockaddr_in);
    if (connect(connect_fd, (struct sockaddr *) &saddr, addr_size) < 0 &&
            errno != EINPROGRESS) {
        log_fatal_exit("connect failed: %s", strerror(errno));
    }

    char saddr_name[32];
    inet_ntop(saddr.sin_family, (void*)&saddr.sin_addr, saddr_name, sizeof(saddr_name));
    log_debug("connecting to: %s:%d", saddr_name, ntohs(saddr.sin_port));

    char buf[16384] = "Hello World\n";
    int buf_len = (int)strlen(buf);
    poll_vec.push_back({connect_fd, POLLOUT, 0});

    SSL *ssl = SSL_new(ctx);
    SSL_set_fd(ssl, connect_fd);
    SSL_set_connect_state(ssl);

    tls_connection_map.insert
        (std::pair<int,tls_connection>
            (connect_fd, tls_connection(connect_fd, ssl)));

    while (true)
    {
        int ret = poll(poll_vec.data(), (int)poll_vec.size(), -1);
        if (ret < 0 && (errno != EAGAIN || errno != EINTR))
        {
            log_fatal_exit("poll failed: %s", strerror(errno));
            exit(9);
        }
        auto poll_events = poll_vec;
        for (auto &pfd : poll_events)
        {
            auto si = tls_connection_map.find(pfd.fd);
            if (si == tls_connection_map.end()) continue;
            tls_connection &conn = si->second;

            if (pfd.revents & (POLLHUP | POLLERR))
            {
                close_connection(conn);
                break;
            }
            else if (conn.state == ssl_none && pfd.revents & POLLOUT)
            {
                int ret = SSL_do_handshake(conn.ssl);
                if (ret < 0) {
                    int ssl_err = SSL_get_error(conn.ssl, ret);
                    update_state(conn, ssl_err);
                }
            }
            else if (conn.state == ssl_handshake_read && pfd.revents & POLLIN)
            {
                int ret = SSL_do_handshake(conn.ssl);
                if (ret < 0) {
                    int ssl_err = SSL_get_error(conn.ssl, ret);
                    update_state(conn, ssl_err);
                } else {
                    update_state(conn, POLLOUT, ssl_app_write);
                }
            }
            else if (conn.state == ssl_handshake_write && pfd.revents & POLLOUT)
            {
                int ret = SSL_do_handshake(conn.ssl);
                if (ret < 0) {
                    int ssl_err = SSL_get_error(conn.ssl, ret);
                    update_state(conn, ssl_err);
                } else {
                    update_state(conn, POLLOUT, ssl_app_write);
                }
            }
            else if (conn.state == ssl_app_write && pfd.revents & POLLOUT)
            {
                int ret = SSL_write(conn.ssl, buf, buf_len);
                if (ret < 0) {
                    int ssl_err = SSL_get_error(conn.ssl, ret);
                    update_state(conn, ssl_err);
                } else {
                    printf("sent: %s", buf);
                    update_state(conn, POLLIN, ssl_app_read);
                }
            }
            else if (conn.state == ssl_app_read && pfd.revents & POLLIN)
            {
                int ret = SSL_read(conn.ssl, buf, sizeof(buf) - 1);
                if (ret < 0) {
                    int ssl_err = SSL_get_error(conn.ssl, ret);
                    update_state(conn, ssl_err);
                } else {
                    buf_len = ret;
                    buf[buf_len] = '\0';
                    printf("received: %s", buf);

                    // TODO - we should probably shutdown gracefully
                    // SSL_shutdown(conn.ssl);
                    close_connection(conn);

                    // exit
                    return;
                }
            }
        }
    }
}

int main(int argc, char **argv)
{
    SSL_library_init();
    tls_echo_client client;
    client.mainloop();
}
<commit_msg>Fix case where handshake completes on first call to SSL_do_handshake<commit_after>//
//  openssl_async_echo_client.cc
//
//  clang++ -std=c++11 openssl_async_echo_client.cc -lcrypto -lssl -o openssl_async_echo_client
//
//  * example of non-blocking TLS
//  * tested with openssl/boringssl
//  * probably leaks
//

#include <netdb.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/poll.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <cstdio>
#include <cstdarg>
#include <cstring>
#include <cerrno>
#include <csignal>
#include <vector>
#include <map>
#include <algorithm>

#include <openssl/crypto.h>
#include <openssl/ssl.h>
#include <openssl/err.h>

static const char* connect_host = "127.0.0.1";
static int connect_port = 8443;

static const char* ssl_cacert_file = "ssl/cacert.pem";

enum ssl_state
{
    ssl_none,
    ssl_handshake_read,
    ssl_handshake_write,
    ssl_app_read,
    ssl_app_write
};

static const char* state_names[] =
{
    "ssl_none",
    "ssl_handshake_read",
    "ssl_handshake_write",
    "ssl_app_read",
    "ssl_app_write"
};

struct tls_connection
{
    tls_connection(int fd, SSL *ssl)
        : fd(fd), ssl(ssl), state(ssl_none) {}
    tls_connection(const tls_connection &o)
        : fd(o.fd), ssl(o.ssl), state(o.state) {}

    int fd;
    SSL *ssl;
    ssl_state state;
};

struct tls_echo_client
{
    std::vector<struct pollfd> poll_vec;
    std::map<int,tls_connection> tls_connection_map;

    void update_state(tls_connection &conn, int events, ssl_state new_state);
    void update_state(tls_connection &conn, int ssl_err);
    void close_connection(tls_connection &conn);
    void mainloop();
};

static void log_prefix(const char* prefix, const char* fmt, va_list args)
{
    std::vector<char> buf(256);
    int len = vsnprintf(buf.data(), buf.capacity(), fmt, args);
    if (len >= (int)buf.capacity()) {
        buf.resize(len + 1);
        vsnprintf(buf.data(), buf.capacity(), fmt, args);
    }
    fprintf(stderr, "%s: %s\n", prefix, buf.data());
}

static void log_fatal_exit(const char* fmt, ...)
{
    va_list args;
    va_start(args, fmt);
    log_prefix("fatal", fmt, args);
    va_end(args);
    exit(9);
}

static void log_debug(const char* fmt, ...)
{
    va_list args;
    va_start(args, fmt);
    log_prefix("debug", fmt, args);
    va_end(args);
}

static int log_tls_errors(const char *str, size_t len, void *bio)
{
    fprintf(stderr, "%s", str);
    return 0;
}

void tls_echo_client::update_state(tls_connection &conn, int events, ssl_state new_state)
{
    log_debug("fd=%d %s -> %s",
              conn.fd, state_names[conn.state], state_names[new_state]);
    conn.state = new_state;
    auto pi = std::find_if(poll_vec.begin(), poll_vec.end(),
                           [&] (const struct pollfd &pfd) { return pfd.fd == conn.fd; });
    if (pi != poll_vec.end()) pi->events = events;
    else log_fatal_exit("file descriptor missing from poll_vec: %d", conn.fd);
}

void tls_echo_client::update_state(tls_connection &conn, int ssl_err)
{
    switch (ssl_err) {
        case SSL_ERROR_WANT_READ:
            update_state(conn, POLLIN, ssl_handshake_read);
            break;
        case SSL_ERROR_WANT_WRITE:
            update_state(conn, POLLOUT, ssl_handshake_write);
            break;
        default:
            log_fatal_exit("unknown tls error: %d", ssl_err);
            break;
    }
}

void tls_echo_client::close_connection(tls_connection &conn)
{
    log_debug("connection closed");
    int fd = conn.fd;
    close(fd);
    auto pi = std::find_if(poll_vec.begin(), poll_vec.end(),
                           [fd] (const struct pollfd &pfd) { return pfd.fd == fd; });
    if (pi != poll_vec.end()) poll_vec.erase(pi);
    tls_connection_map.erase(fd);
}

void tls_echo_client::mainloop()
{
    SSL_CTX *ctx = SSL_CTX_new(TLSv1_client_method());

    if ((!SSL_CTX_load_verify_locations(ctx, ssl_cacert_file, NULL)) ||
        (!SSL_CTX_set_default_verify_paths(ctx))) {
        ERR_print_errors_cb(log_tls_errors, NULL);
        log_fatal_exit("failed to load cacert: %s", ssl_cacert_file);
    } else {
        log_debug("loaded cacert: %s", ssl_cacert_file);
    }
    SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
    SSL_CTX_set_verify_depth(ctx, 9);

    sockaddr_in saddr;
    memset(&saddr, 0, sizeof(saddr));
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(connect_port);

    struct hostent *he = gethostbyname(connect_host);
    if (he) {
        if (he->h_addrtype == AF_INET) {
            memcpy(&saddr.sin_addr, he->h_addr_list[0], he->h_length);
        } else {
            log_fatal_exit("unknown address type: %s", he->h_addrtype);
        }
    } else {
        log_fatal_exit("unknown host %s", connect_host);
    }

    int connect_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (connect_fd < 0) {
        log_fatal_exit("socket failed: %s", strerror(errno));
    }
    if (fcntl(connect_fd, F_SETFD, FD_CLOEXEC) < 0) {
        log_fatal_exit("fcntl(F_SETFD, FD_CLOEXEC) failed: %s", strerror(errno));
    }
    if (fcntl(connect_fd, F_SETFL, O_NONBLOCK) < 0) {
        log_fatal_exit("fcntl(F_SETFL, O_NONBLOCK) failed: %s", strerror(errno));
    }
    socklen_t addr_size = sizeof(sockaddr_in);
    if (connect(connect_fd, (struct sockaddr *) &saddr, addr_size) < 0 &&
            errno != EINPROGRESS) {
        log_fatal_exit("connect failed: %s", strerror(errno));
    }

    char saddr_name[32];
    inet_ntop(saddr.sin_family, (void*)&saddr.sin_addr, saddr_name, sizeof(saddr_name));
    log_debug("connecting to: %s:%d", saddr_name, ntohs(saddr.sin_port));

    char buf[16384] = "Hello World\n";
    int buf_len = (int)strlen(buf);
    poll_vec.push_back({connect_fd, POLLOUT, 0});

    SSL *ssl = SSL_new(ctx);
    SSL_set_fd(ssl, connect_fd);
    SSL_set_connect_state(ssl);

    tls_connection_map.insert
        (std::pair<int,tls_connection>
            (connect_fd, tls_connection(connect_fd, ssl)));

    while (true)
    {
        int ret = poll(poll_vec.data(), (int)poll_vec.size(), -1);
        if (ret < 0 && (errno != EAGAIN || errno != EINTR))
        {
            log_fatal_exit("poll failed: %s", strerror(errno));
            exit(9);
        }
        auto poll_events = poll_vec;
        for (auto &pfd : poll_events)
        {
            auto si = tls_connection_map.find(pfd.fd);
            if (si == tls_connection_map.end()) continue;
            tls_connection &conn = si->second;

            if (pfd.revents & (POLLHUP | POLLERR))
            {
                close_connection(conn);
                break;
            }
            else if (conn.state == ssl_none && pfd.revents & POLLOUT)
            {
                int ret = SSL_do_handshake(conn.ssl);
                if (ret < 0) {
                    int ssl_err = SSL_get_error(conn.ssl, ret);
                    update_state(conn, ssl_err);
                } else {
                    update_state(conn, POLLOUT, ssl_app_write);
                }
            }
            else if (conn.state == ssl_handshake_read && pfd.revents & POLLIN)
            {
                int ret = SSL_do_handshake(conn.ssl);
                if (ret < 0) {
                    int ssl_err = SSL_get_error(conn.ssl, ret);
                    update_state(conn, ssl_err);
                } else {
                    update_state(conn, POLLOUT, ssl_app_write);
                }
            }
            else if (conn.state == ssl_handshake_write && pfd.revents & POLLOUT)
            {
                int ret = SSL_do_handshake(conn.ssl);
                if (ret < 0) {
                    int ssl_err = SSL_get_error(conn.ssl, ret);
                    update_state(conn, ssl_err);
                } else {
                    update_state(conn, POLLOUT, ssl_app_write);
                }
            }
            else if (conn.state == ssl_app_write && pfd.revents & POLLOUT)
            {
                int ret = SSL_write(conn.ssl, buf, buf_len);
                if (ret < 0) {
                    int ssl_err = SSL_get_error(conn.ssl, ret);
                    update_state(conn, ssl_err);
                } else {
                    printf("sent: %s", buf);
                    update_state(conn, POLLIN, ssl_app_read);
                }
            }
            else if (conn.state == ssl_app_read && pfd.revents & POLLIN)
            {
                int ret = SSL_read(conn.ssl, buf, sizeof(buf) - 1);
                if (ret < 0) {
                    int ssl_err = SSL_get_error(conn.ssl, ret);
                    update_state(conn, ssl_err);
                } else {
                    buf_len = ret;
                    buf[buf_len] = '\0';
                    printf("received: %s", buf);

                    // TODO - we should probably shutdown gracefully
                    // SSL_shutdown(conn.ssl);
                    close_connection(conn);

                    // exit
                    return;
                }
            }
        }
    }
}

int main(int argc, char **argv)
{
    SSL_library_init();
    tls_echo_client client;
    client.mainloop();
}
<|endoftext|>


<commit_before>#include "master.hpp"

namespace factor {

HMODULE hFactorDll;

bool set_memory_locked(cell base, cell size, bool locked) {
  int prot = locked ? PAGE_NOACCESS : PAGE_READWRITE;
  DWORD ignore;
  int status = VirtualProtect((char*)base, size, prot, &ignore);
  return status != 0;
}

void factor_vm::init_ffi() {
  hFactorDll = GetModuleHandle(NULL);
  if (!hFactorDll)
    fatal_error("GetModuleHandle() failed", 0);
}

void factor_vm::ffi_dlopen(dll* dll) {
  dll->handle = LoadLibraryEx((WCHAR*)alien_offset(dll->path), NULL, 0);
}

cell factor_vm::ffi_dlsym(dll* dll, symbol_char* symbol) {
  return (cell)GetProcAddress(dll ? (HMODULE) dll->handle : hFactorDll,
                              symbol);
}

cell factor_vm::ffi_dlsym_raw(dll* dll, symbol_char* symbol) {
  return ffi_dlsym(dll, symbol);
}

void factor_vm::ffi_dlclose(dll* dll) {
  FreeLibrary((HMODULE) dll->handle);
  dll->handle = NULL;
}

BOOL factor_vm::windows_stat(vm_char* path) {
  BY_HANDLE_FILE_INFORMATION bhfi;
  HANDLE h = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ, NULL,
                         OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);

  if (h == INVALID_HANDLE_VALUE) {
    // FindFirstFile is the only call that can stat c:\pagefile.sys
    WIN32_FIND_DATA st;
    HANDLE h;

    if (INVALID_HANDLE_VALUE == (h = FindFirstFile(path, &st)))
      return false;
    FindClose(h);
    return true;
  }
  BOOL ret = GetFileInformationByHandle(h, &bhfi);
  CloseHandle(h);
  return ret;
}

/* You must free() this yourself. */
const vm_char* factor_vm::default_image_path() {
  vm_char full_path[MAX_UNICODE_PATH];
  vm_char* ptr;
  vm_char temp_path[MAX_UNICODE_PATH];

  if (!GetModuleFileName(NULL, full_path, MAX_UNICODE_PATH))
    fatal_error("GetModuleFileName() failed", 0);

  if ((ptr = wcsrchr(full_path, '.')))
    *ptr = 0;

  wcsncpy(temp_path, full_path, MAX_UNICODE_PATH - 1);
  size_t full_path_len = wcslen(full_path);
  if (full_path_len < MAX_UNICODE_PATH - 1)
    wcsncat(temp_path, L".image", MAX_UNICODE_PATH - full_path_len - 1);
  temp_path[MAX_UNICODE_PATH - 1] = 0;

  return safe_strdup(temp_path);
}

/* You must free() this yourself. */
const vm_char* factor_vm::vm_executable_path() {
  vm_char full_path[MAX_UNICODE_PATH];
  if (!GetModuleFileName(NULL, full_path, MAX_UNICODE_PATH))
    fatal_error("GetModuleFileName() failed", 0);
  return safe_strdup(full_path);
}

void factor_vm::primitive_existsp() {
  vm_char* path = untag_check<byte_array>(ctx->pop())->data<vm_char>();
  ctx->push(tag_boolean(windows_stat(path)));
}

segment::segment(cell size_, bool executable_p) {
  size = size_;

  char* mem;

  if ((mem = (char*)VirtualAlloc(
           NULL, getpagesize() * 2 + size, MEM_COMMIT,
           executable_p ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE)) ==
      0) {
    out_of_memory("VirtualAlloc");
  }

  start = (cell)mem + getpagesize();
  end = start + size;

  set_border_locked(true);
}

void segment::set_border_locked(bool locked) {
  int pagesize = getpagesize();
  cell lo = start - pagesize;
  if (!set_memory_locked(lo, pagesize, locked)) {
    fatal_error("Cannot (un)protect low guard page", lo);
  }

  cell hi = end;
  if (!set_memory_locked(hi, pagesize, locked)) {
    fatal_error("Cannot (un)protect high guard page", hi);
  }
}

segment::~segment() {
  SYSTEM_INFO si;
  GetSystemInfo(&si);
  if (!VirtualFree((void*)(start - si.dwPageSize), 0, MEM_RELEASE))
    fatal_error("Segment deallocation failed", 0);
}

long getpagesize() {
  static long g_pagesize = 0;
  if (!g_pagesize) {
    SYSTEM_INFO system_info;
    GetSystemInfo(&system_info);
    g_pagesize = system_info.dwPageSize;
  }
  return g_pagesize;
}

bool move_file(const vm_char* path1, const vm_char* path2) {
  return MoveFileEx((path1), (path2), MOVEFILE_REPLACE_EXISTING);
}

void factor_vm::init_signals() {}

THREADHANDLE start_thread(void* (*start_routine)(void*), void* args) {
  return (void*)CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) start_routine,
                             args, 0, 0);
}

uint64_t nano_count() {
  static double scale_factor;

  static uint32_t hi = 0;
  static uint32_t lo = 0;

  LARGE_INTEGER count;
  BOOL ret = QueryPerformanceCounter(&count);
  if (ret == 0)
    fatal_error("QueryPerformanceCounter", 0);

  if (scale_factor == 0.0) {
    LARGE_INTEGER frequency;
    BOOL ret = QueryPerformanceFrequency(&frequency);
    if (ret == 0)
      fatal_error("QueryPerformanceFrequency", 0);
    scale_factor = (1000000000.0 / frequency.QuadPart);
  }

#ifdef FACTOR_64
  hi = count.HighPart;
#else
  /* On VirtualBox, QueryPerformanceCounter does not increment
	the high part every time the low part overflows.  Workaround. */
  if (lo > count.LowPart)
    hi++;
#endif
  lo = count.LowPart;

  return (uint64_t)((((uint64_t)hi << 32) | (uint64_t)lo) * scale_factor);
}

void sleep_nanos(uint64_t nsec) { Sleep((DWORD)(nsec / 1000000)); }

typedef enum _EXCEPTION_DISPOSITION {
  ExceptionContinueExecution = 0,
  ExceptionContinueSearch = 1,
  ExceptionNestedException = 2,
  ExceptionCollidedUnwind = 3
} EXCEPTION_DISPOSITION;

LONG factor_vm::exception_handler(PEXCEPTION_RECORD e, void* frame, PCONTEXT c,
                                  void* dispatch) {
  switch (e->ExceptionCode) {
    case EXCEPTION_ACCESS_VIOLATION:
      signal_fault_addr = e->ExceptionInformation[1];
      signal_fault_pc = c->EIP;
      verify_memory_protection_error(signal_fault_addr);
      dispatch_signal_handler((cell*)&c->ESP, (cell*)&c->EIP,
                              (cell)factor::memory_signal_handler_impl);
      break;

    case STATUS_FLOAT_DENORMAL_OPERAND:
    case STATUS_FLOAT_DIVIDE_BY_ZERO:
    case STATUS_FLOAT_INEXACT_RESULT:
    case STATUS_FLOAT_INVALID_OPERATION:
    case STATUS_FLOAT_OVERFLOW:
    case STATUS_FLOAT_STACK_CHECK:
    case STATUS_FLOAT_UNDERFLOW:
    case STATUS_FLOAT_MULTIPLE_FAULTS:
    case STATUS_FLOAT_MULTIPLE_TRAPS:
#ifdef FACTOR_64
      signal_fpu_status = fpu_status(MXCSR(c));
#else
      signal_fpu_status = fpu_status(X87SW(c) | MXCSR(c));

      /* This seems to have no effect */
      X87SW(c) = 0;
#endif
      MXCSR(c) &= 0xffffffc0;
      dispatch_signal_handler((cell*)&c->ESP, (cell*)&c->EIP,
                              (cell)factor::fp_signal_handler_impl);
      break;
    default:
      signal_number = e->ExceptionCode;
      dispatch_signal_handler((cell*)&c->ESP, (cell*)&c->EIP,
                              (cell)factor::synchronous_signal_handler_impl);
      break;
  }
  return ExceptionContinueExecution;
}

VM_C_API LONG exception_handler(PEXCEPTION_RECORD e, void* frame, PCONTEXT c,
                                void* dispatch) {
  factor_vm* vm = current_vm_p();
  if (factor_vm::fatal_erroring_p || !vm)
    return ExceptionContinueSearch;
  return vm->exception_handler(e, frame, c, dispatch);
}

/* On Unix SIGINT (ctrl-c) automatically interrupts blocking io system
   calls. It doesn't on Windows, so we need to manually send some
   cancellation requests to unblock the thread. */
VOID CALLBACK dummy_cb (ULONG_PTR dwParam) { }

// CancelSynchronousIo is not in Windows XP
#if _WIN32_WINNT >= 0x0600
static void wake_up_thread(HANDLE thread) {
  if (!CancelSynchronousIo(thread)) {
    DWORD err = GetLastError();
    /* CancelSynchronousIo() didn't find anything to cancel, let's try
       with QueueUserAPC() instead. */
    if (err == ERROR_NOT_FOUND) {
      if (!QueueUserAPC(&dummy_cb, thread, NULL)) {
        fatal_error("QueueUserAPC() failed", GetLastError());
      }
    } else {
      fatal_error("CancelSynchronousIo() failed", err);
    }
  }
}
#else
static void wake_up_thread(HANDLE thread) {}
#endif

static BOOL WINAPI ctrl_handler(DWORD dwCtrlType) {
  switch (dwCtrlType) {
    case CTRL_C_EVENT: {
      /* The CtrlHandler runs in its own thread without stopping the main
         thread. Since in practice nobody uses the multi-VM stuff yet, we just
         grab the first VM we can get. This will not be a good idea when we
         actually support native threads. */
      FACTOR_ASSERT(thread_vms.size() == 1);
      factor_vm* vm = thread_vms.begin()->second;
      vm->safepoint.enqueue_fep(vm);

      /* Before leaving the ctrl_handler, try and wake up the main
         thread. */
      wake_up_thread(factor::boot_thread);
      return TRUE;
    }
    default:
      return FALSE;
  }
}

void open_console() { handle_ctrl_c(); }

void ignore_ctrl_c() {
  SetConsoleCtrlHandler(factor::ctrl_handler, FALSE);
}

void handle_ctrl_c() {
  SetConsoleCtrlHandler(factor::ctrl_handler, TRUE);
}

void lock_console() {}

void unlock_console() {}

void close_console() {}

cell get_thread_pc(THREADHANDLE th) {
  DWORD suscount = SuspendThread(th);
  FACTOR_ASSERT(suscount == 0);

  CONTEXT context;
  memset((void*)&context, 0, sizeof(CONTEXT));
  context.ContextFlags = CONTEXT_CONTROL;
  BOOL context_ok = GetThreadContext(th, &context);
  FACTOR_ASSERT(context_ok);

  suscount = ResumeThread(th);
  FACTOR_ASSERT(suscount == 1);
  return context.EIP;
}

void factor_vm::sampler_thread_loop() {
  LARGE_INTEGER counter, new_counter, units_per_second;
  DWORD ok;

  ok = QueryPerformanceFrequency(&units_per_second);
  FACTOR_ASSERT(ok);

  ok = QueryPerformanceCounter(&counter);
  FACTOR_ASSERT(ok);

  counter.QuadPart *= samples_per_second;
  while (atomic::load(&sampling_profiler_p)) {
    SwitchToThread();
    ok = QueryPerformanceCounter(&new_counter);
    FACTOR_ASSERT(ok);
    new_counter.QuadPart *= samples_per_second;
    cell samples = 0;
    while (new_counter.QuadPart - counter.QuadPart >
           units_per_second.QuadPart) {
      ++samples;
      counter.QuadPart += units_per_second.QuadPart;
    }
    if (samples == 0)
      continue;

    cell pc = get_thread_pc(thread);
    safepoint.enqueue_samples(this, samples, pc, false);
  }
}

static DWORD WINAPI sampler_thread_entry(LPVOID parent_vm) {
  static_cast<factor_vm*>(parent_vm)->sampler_thread_loop();
  return 0;
}

void factor_vm::start_sampling_profiler_timer() {
  sampler_thread = CreateThread(NULL, 0, &sampler_thread_entry,
                                static_cast<LPVOID>(this), 0, NULL);
}

void factor_vm::end_sampling_profiler_timer() {
  atomic::store(&sampling_profiler_p, false);
  DWORD wait_result =
      WaitForSingleObject(sampler_thread, 3000 * (DWORD) samples_per_second);
  if (wait_result != WAIT_OBJECT_0)
    TerminateThread(sampler_thread, 0);
  sampler_thread = NULL;
}

void abort() { ::abort(); }

}
<commit_msg>vm: MoveFileEx returns BOOL which needs help converting to C++ bool.<commit_after>#include "master.hpp"

namespace factor {

HMODULE hFactorDll;

bool set_memory_locked(cell base, cell size, bool locked) {
  int prot = locked ? PAGE_NOACCESS : PAGE_READWRITE;
  DWORD ignore;
  int status = VirtualProtect((char*)base, size, prot, &ignore);
  return status != 0;
}

void factor_vm::init_ffi() {
  hFactorDll = GetModuleHandle(NULL);
  if (!hFactorDll)
    fatal_error("GetModuleHandle() failed", 0);
}

void factor_vm::ffi_dlopen(dll* dll) {
  dll->handle = LoadLibraryEx((WCHAR*)alien_offset(dll->path), NULL, 0);
}

cell factor_vm::ffi_dlsym(dll* dll, symbol_char* symbol) {
  return (cell)GetProcAddress(dll ? (HMODULE) dll->handle : hFactorDll,
                              symbol);
}

cell factor_vm::ffi_dlsym_raw(dll* dll, symbol_char* symbol) {
  return ffi_dlsym(dll, symbol);
}

void factor_vm::ffi_dlclose(dll* dll) {
  FreeLibrary((HMODULE) dll->handle);
  dll->handle = NULL;
}

BOOL factor_vm::windows_stat(vm_char* path) {
  BY_HANDLE_FILE_INFORMATION bhfi;
  HANDLE h = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ, NULL,
                         OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);

  if (h == INVALID_HANDLE_VALUE) {
    // FindFirstFile is the only call that can stat c:\pagefile.sys
    WIN32_FIND_DATA st;
    HANDLE h;

    if (INVALID_HANDLE_VALUE == (h = FindFirstFile(path, &st)))
      return false;
    FindClose(h);
    return true;
  }
  BOOL ret = GetFileInformationByHandle(h, &bhfi);
  CloseHandle(h);
  return ret;
}

/* You must free() this yourself. */
const vm_char* factor_vm::default_image_path() {
  vm_char full_path[MAX_UNICODE_PATH];
  vm_char* ptr;
  vm_char temp_path[MAX_UNICODE_PATH];

  if (!GetModuleFileName(NULL, full_path, MAX_UNICODE_PATH))
    fatal_error("GetModuleFileName() failed", 0);

  if ((ptr = wcsrchr(full_path, '.')))
    *ptr = 0;

  wcsncpy(temp_path, full_path, MAX_UNICODE_PATH - 1);
  size_t full_path_len = wcslen(full_path);
  if (full_path_len < MAX_UNICODE_PATH - 1)
    wcsncat(temp_path, L".image", MAX_UNICODE_PATH - full_path_len - 1);
  temp_path[MAX_UNICODE_PATH - 1] = 0;

  return safe_strdup(temp_path);
}

/* You must free() this yourself. */
const vm_char* factor_vm::vm_executable_path() {
  vm_char full_path[MAX_UNICODE_PATH];
  if (!GetModuleFileName(NULL, full_path, MAX_UNICODE_PATH))
    fatal_error("GetModuleFileName() failed", 0);
  return safe_strdup(full_path);
}

void factor_vm::primitive_existsp() {
  vm_char* path = untag_check<byte_array>(ctx->pop())->data<vm_char>();
  ctx->push(tag_boolean(windows_stat(path)));
}

segment::segment(cell size_, bool executable_p) {
  size = size_;

  char* mem;

  if ((mem = (char*)VirtualAlloc(
           NULL, getpagesize() * 2 + size, MEM_COMMIT,
           executable_p ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE)) ==
      0) {
    out_of_memory("VirtualAlloc");
  }

  start = (cell)mem + getpagesize();
  end = start + size;

  set_border_locked(true);
}

void segment::set_border_locked(bool locked) {
  int pagesize = getpagesize();
  cell lo = start - pagesize;
  if (!set_memory_locked(lo, pagesize, locked)) {
    fatal_error("Cannot (un)protect low guard page", lo);
  }

  cell hi = end;
  if (!set_memory_locked(hi, pagesize, locked)) {
    fatal_error("Cannot (un)protect high guard page", hi);
  }
}

segment::~segment() {
  SYSTEM_INFO si;
  GetSystemInfo(&si);
  if (!VirtualFree((void*)(start - si.dwPageSize), 0, MEM_RELEASE))
    fatal_error("Segment deallocation failed", 0);
}

long getpagesize() {
  static long g_pagesize = 0;
  if (!g_pagesize) {
    SYSTEM_INFO system_info;
    GetSystemInfo(&system_info);
    g_pagesize = system_info.dwPageSize;
  }
  return g_pagesize;
}

// MoveFileEx returns FALSE on fail
bool move_file(const vm_char* path1, const vm_char* path2) {
  return !(MoveFileEx((path1), (path2), MOVEFILE_REPLACE_EXISTING) == FALSE);
}

void factor_vm::init_signals() {}

THREADHANDLE start_thread(void* (*start_routine)(void*), void* args) {
  return (void*)CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) start_routine,
                             args, 0, 0);
}

uint64_t nano_count() {
  static double scale_factor;

  static uint32_t hi = 0;
  static uint32_t lo = 0;

  LARGE_INTEGER count;
  BOOL ret = QueryPerformanceCounter(&count);
  if (ret == 0)
    fatal_error("QueryPerformanceCounter", 0);

  if (scale_factor == 0.0) {
    LARGE_INTEGER frequency;
    BOOL ret = QueryPerformanceFrequency(&frequency);
    if (ret == 0)
      fatal_error("QueryPerformanceFrequency", 0);
    scale_factor = (1000000000.0 / frequency.QuadPart);
  }

#ifdef FACTOR_64
  hi = count.HighPart;
#else
  /* On VirtualBox, QueryPerformanceCounter does not increment
	the high part every time the low part overflows.  Workaround. */
  if (lo > count.LowPart)
    hi++;
#endif
  lo = count.LowPart;

  return (uint64_t)((((uint64_t)hi << 32) | (uint64_t)lo) * scale_factor);
}

void sleep_nanos(uint64_t nsec) { Sleep((DWORD)(nsec / 1000000)); }

typedef enum _EXCEPTION_DISPOSITION {
  ExceptionContinueExecution = 0,
  ExceptionContinueSearch = 1,
  ExceptionNestedException = 2,
  ExceptionCollidedUnwind = 3
} EXCEPTION_DISPOSITION;

LONG factor_vm::exception_handler(PEXCEPTION_RECORD e, void* frame, PCONTEXT c,
                                  void* dispatch) {
  switch (e->ExceptionCode) {
    case EXCEPTION_ACCESS_VIOLATION:
      signal_fault_addr = e->ExceptionInformation[1];
      signal_fault_pc = c->EIP;
      verify_memory_protection_error(signal_fault_addr);
      dispatch_signal_handler((cell*)&c->ESP, (cell*)&c->EIP,
                              (cell)factor::memory_signal_handler_impl);
      break;

    case STATUS_FLOAT_DENORMAL_OPERAND:
    case STATUS_FLOAT_DIVIDE_BY_ZERO:
    case STATUS_FLOAT_INEXACT_RESULT:
    case STATUS_FLOAT_INVALID_OPERATION:
    case STATUS_FLOAT_OVERFLOW:
    case STATUS_FLOAT_STACK_CHECK:
    case STATUS_FLOAT_UNDERFLOW:
    case STATUS_FLOAT_MULTIPLE_FAULTS:
    case STATUS_FLOAT_MULTIPLE_TRAPS:
#ifdef FACTOR_64
      signal_fpu_status = fpu_status(MXCSR(c));
#else
      signal_fpu_status = fpu_status(X87SW(c) | MXCSR(c));

      /* This seems to have no effect */
      X87SW(c) = 0;
#endif
      MXCSR(c) &= 0xffffffc0;
      dispatch_signal_handler((cell*)&c->ESP, (cell*)&c->EIP,
                              (cell)factor::fp_signal_handler_impl);
      break;
    default:
      signal_number = e->ExceptionCode;
      dispatch_signal_handler((cell*)&c->ESP, (cell*)&c->EIP,
                              (cell)factor::synchronous_signal_handler_impl);
      break;
  }
  return ExceptionContinueExecution;
}

VM_C_API LONG exception_handler(PEXCEPTION_RECORD e, void* frame, PCONTEXT c,
                                void* dispatch) {
  factor_vm* vm = current_vm_p();
  if (factor_vm::fatal_erroring_p || !vm)
    return ExceptionContinueSearch;
  return vm->exception_handler(e, frame, c, dispatch);
}

/* On Unix SIGINT (ctrl-c) automatically interrupts blocking io system
   calls. It doesn't on Windows, so we need to manually send some
   cancellation requests to unblock the thread. */
VOID CALLBACK dummy_cb (ULONG_PTR dwParam) { }

// CancelSynchronousIo is not in Windows XP
#if _WIN32_WINNT >= 0x0600
static void wake_up_thread(HANDLE thread) {
  if (!CancelSynchronousIo(thread)) {
    DWORD err = GetLastError();
    /* CancelSynchronousIo() didn't find anything to cancel, let's try
       with QueueUserAPC() instead. */
    if (err == ERROR_NOT_FOUND) {
      if (!QueueUserAPC(&dummy_cb, thread, NULL)) {
        fatal_error("QueueUserAPC() failed", GetLastError());
      }
    } else {
      fatal_error("CancelSynchronousIo() failed", err);
    }
  }
}
#else
static void wake_up_thread(HANDLE thread) {}
#endif

static BOOL WINAPI ctrl_handler(DWORD dwCtrlType) {
  switch (dwCtrlType) {
    case CTRL_C_EVENT: {
      /* The CtrlHandler runs in its own thread without stopping the main
         thread. Since in practice nobody uses the multi-VM stuff yet, we just
         grab the first VM we can get. This will not be a good idea when we
         actually support native threads. */
      FACTOR_ASSERT(thread_vms.size() == 1);
      factor_vm* vm = thread_vms.begin()->second;
      vm->safepoint.enqueue_fep(vm);

      /* Before leaving the ctrl_handler, try and wake up the main
         thread. */
      wake_up_thread(factor::boot_thread);
      return TRUE;
    }
    default:
      return FALSE;
  }
}

void open_console() { handle_ctrl_c(); }

void ignore_ctrl_c() {
  SetConsoleCtrlHandler(factor::ctrl_handler, FALSE);
}

void handle_ctrl_c() {
  SetConsoleCtrlHandler(factor::ctrl_handler, TRUE);
}

void lock_console() {}

void unlock_console() {}

void close_console() {}

cell get_thread_pc(THREADHANDLE th) {
  DWORD suscount = SuspendThread(th);
  FACTOR_ASSERT(suscount == 0);

  CONTEXT context;
  memset((void*)&context, 0, sizeof(CONTEXT));
  context.ContextFlags = CONTEXT_CONTROL;
  BOOL context_ok = GetThreadContext(th, &context);
  FACTOR_ASSERT(context_ok);

  suscount = ResumeThread(th);
  FACTOR_ASSERT(suscount == 1);
  return context.EIP;
}

void factor_vm::sampler_thread_loop() {
  LARGE_INTEGER counter, new_counter, units_per_second;
  DWORD ok;

  ok = QueryPerformanceFrequency(&units_per_second);
  FACTOR_ASSERT(ok);

  ok = QueryPerformanceCounter(&counter);
  FACTOR_ASSERT(ok);

  counter.QuadPart *= samples_per_second;
  while (atomic::load(&sampling_profiler_p)) {
    SwitchToThread();
    ok = QueryPerformanceCounter(&new_counter);
    FACTOR_ASSERT(ok);
    new_counter.QuadPart *= samples_per_second;
    cell samples = 0;
    while (new_counter.QuadPart - counter.QuadPart >
           units_per_second.QuadPart) {
      ++samples;
      counter.QuadPart += units_per_second.QuadPart;
    }
    if (samples == 0)
      continue;

    cell pc = get_thread_pc(thread);
    safepoint.enqueue_samples(this, samples, pc, false);
  }
}

static DWORD WINAPI sampler_thread_entry(LPVOID parent_vm) {
  static_cast<factor_vm*>(parent_vm)->sampler_thread_loop();
  return 0;
}

void factor_vm::start_sampling_profiler_timer() {
  sampler_thread = CreateThread(NULL, 0, &sampler_thread_entry,
                                static_cast<LPVOID>(this), 0, NULL);
}

void factor_vm::end_sampling_profiler_timer() {
  atomic::store(&sampling_profiler_p, false);
  DWORD wait_result =
      WaitForSingleObject(sampler_thread, 3000 * (DWORD) samples_per_second);
  if (wait_result != WAIT_OBJECT_0)
    TerminateThread(sampler_thread, 0);
  sampler_thread = NULL;
}

void abort() { ::abort(); }

}
<|endoftext|>


<commit_before>/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
 *
 * This library is open source and may be redistributed and/or modified under
 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
 * (at your option) any later version.  The full license is in LICENSE file
 * included with this distribution, and on the openscenegraph.org website.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * OpenSceneGraph Public License for more details.
*/

#include <osg/OperationThread>
#include <osg/GraphicsContext>
#include <osg/Notify>

using namespace osg;
using namespace OpenThreads;

struct BlockOperation : public Operation, public Block
{
    BlockOperation():
        Operation("Block",false)
    {
        reset();
    }

    virtual void release()
    {
        Block::release();
    }

    virtual void operator () (Object*)
    {
        glFlush();
        Block::release();
    }
};

/////////////////////////////////////////////////////////////////////////////
//
//  OperationsQueue
//

OperationQueue::OperationQueue():
    osg::Referenced(true)
{
    _currentOperationIterator = _operations.begin();
    _operationsBlock = new RefBlock;
}

OperationQueue::~OperationQueue()
{
}

bool OperationQueue::empty()
{

  OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_operationsMutex);
  return _operations.empty();
}

unsigned int OperationQueue::getNumOperationsInQueue()
{
  OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_operationsMutex);
  return static_cast<unsigned int>(_operations.size());
}

ref_ptr<Operation> OperationQueue::getNextOperation(bool blockIfEmpty)
{
    if (blockIfEmpty && _operations.empty())
    {
        _operationsBlock->block();
    }

    OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_operationsMutex);

    if (_operations.empty()) return osg::ref_ptr<Operation>();

    if (_currentOperationIterator == _operations.end())
    {
        // iterator at end of operations so reset to beginning.
        _currentOperationIterator = _operations.begin();
    }

    ref_ptr<Operation> currentOperation = *_currentOperationIterator;

    if (!currentOperation->getKeep())
    {
        // OSG_INFO<<"removing "<<currentOperation->getName()<<std::endl;

        // remove it from the operations queue
        _currentOperationIterator = _operations.erase(_currentOperationIterator);

        // OSG_INFO<<"size "<<_operations.size()<<std::endl;

        if (_operations.empty())
        {
           // OSG_INFO<<"setting block "<<_operations.size()<<std::endl;
           _operationsBlock->set(false);
        }
    }
    else
    {
        // OSG_INFO<<"increment "<<_currentOperation->getName()<<std::endl;

        // move on to the next operation in the list.
        ++_currentOperationIterator;
    }

    return currentOperation;
}

void OperationQueue::add(Operation* operation)
{
    OSG_INFO<<"Doing add"<<std::endl;

    // acquire the lock on the operations queue to prevent anyone else for modifying it at the same time
    OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_operationsMutex);

    // add the operation to the end of the list
    _operations.push_back(operation);

    _operationsBlock->set(true);
}

void OperationQueue::remove(Operation* operation)
{
    OSG_INFO<<"Doing remove operation"<<std::endl;

    // acquire the lock on the operations queue to prevent anyone else for modifying it at the same time
    OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_operationsMutex);

    for(Operations::iterator itr = _operations.begin();
        itr!=_operations.end();)
    {
        if ((*itr)==operation)
        {
            bool needToResetCurrentIterator = (_currentOperationIterator == itr);

            itr = _operations.erase(itr);

            if (needToResetCurrentIterator) _currentOperationIterator = itr;

        }
        else ++itr;
    }
}

void OperationQueue::remove(const std::string& name)
{
    OSG_INFO<<"Doing remove named operation"<<std::endl;

    // acquire the lock on the operations queue to prevent anyone else for modifying it at the same time
    OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_operationsMutex);

    // find the remove all operations with specified name
    for(Operations::iterator itr = _operations.begin();
        itr!=_operations.end();)
    {
        if ((*itr)->getName()==name)
        {
            bool needToResetCurrentIterator = (_currentOperationIterator == itr);

            itr = _operations.erase(itr);

            if (needToResetCurrentIterator) _currentOperationIterator = itr;
        }
        else ++itr;
    }

    if (_operations.empty())
    {
        _operationsBlock->set(false);
    }
}

void OperationQueue::removeAllOperations()
{
    OSG_INFO<<"Doing remove all operations"<<std::endl;

    OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_operationsMutex);

    _operations.clear();

    // reset current operator.
    _currentOperationIterator = _operations.begin();

    if (_operations.empty())
    {
        _operationsBlock->set(false);
    }
}

void OperationQueue::runOperations(Object* callingObject)
{
    OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_operationsMutex);

    // reset current operation iterator to beginning if at end.
    if (_currentOperationIterator==_operations.end()) _currentOperationIterator = _operations.begin();

    for(;
        _currentOperationIterator != _operations.end();
        )
    {
        ref_ptr<Operation> operation = *_currentOperationIterator;

        if (!operation->getKeep())
        {
            _currentOperationIterator = _operations.erase(_currentOperationIterator);
        }
        else
        {
            ++_currentOperationIterator;
        }

        // OSG_INFO<<"Doing op "<<_currentOperation->getName()<<" "<<this<<std::endl;

        // call the graphics operation.
        (*operation)(callingObject);
    }

    if (_operations.empty())
    {
        _operationsBlock->set(false);
    }
}

void OperationQueue::releaseOperationsBlock()
{
    _operationsBlock->release();
}

 void OperationQueue::releaseAllOperations()
{
    OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_operationsMutex);

    for(Operations::iterator itr = _operations.begin();
        itr!=_operations.end();
        ++itr)
    {
        (*itr)->release();
    }
}

void OperationQueue::addOperationThread(OperationThread* thread)
{
    _operationThreads.insert(thread);
}

void OperationQueue::removeOperationThread(OperationThread* thread)
{
    _operationThreads.erase(thread);
}

/////////////////////////////////////////////////////////////////////////////
//
//  OperationThread
//

OperationThread::OperationThread():
    osg::Referenced(true),
    _parent(0),
    _done(0)
{
    setOperationQueue(new OperationQueue);
}

OperationThread::~OperationThread()
{
    //OSG_NOTICE<<"Destructing graphics thread "<<this<<std::endl;

    cancel();

    //OSG_NOTICE<<"Done Destructing graphics thread "<<this<<std::endl;
}

void OperationThread::setOperationQueue(OperationQueue* opq)
{
    OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_threadMutex);

    if (_operationQueue == opq) return;

    if (_operationQueue.valid()) _operationQueue->removeOperationThread(this);

    _operationQueue = opq;

    if (_operationQueue.valid()) _operationQueue->addOperationThread(this);
}

void OperationThread::setDone(bool done)
{
    unsigned d = done?0:1;
    if (_done==d) return;

    _done.exchange(d);

    if (done)
    {
        OSG_INFO<<"set done "<<this<<std::endl;

        {
            OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_threadMutex);
            if (_currentOperation.valid())
            {
                OSG_INFO<<"releasing "<<_currentOperation.get()<<std::endl;
                _currentOperation->release();
            }
        }

        if (_operationQueue.valid()) _operationQueue->releaseOperationsBlock();
    }
}

int OperationThread::cancel()
{
    OSG_INFO<<"Cancelling OperationThread "<<this<<" isRunning()="<<isRunning()<<std::endl;

    int result = 0;
    if( isRunning() )
    {

        _done.exchange(1);

        OSG_INFO<<"   Doing cancel "<<this<<std::endl;

        {
            OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_threadMutex);

            if (_operationQueue.valid())
            {
                 _operationQueue->releaseOperationsBlock();
                //_operationQueue->releaseAllOperations();
            }

            if (_currentOperation.valid()) _currentOperation->release();
        }

        // then wait for the the thread to stop running.
        join();
    }

    OSG_INFO<<"  OperationThread::cancel() thread cancelled "<<this<<" isRunning()="<<isRunning()<<std::endl;

    return result;
}

void OperationThread::add(Operation* operation)
{
    OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_threadMutex);
    if (!_operationQueue) _operationQueue = new OperationQueue;
    _operationQueue->add(operation);
}

void OperationThread::remove(Operation* operation)
{
    OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_threadMutex);
    if (_operationQueue.valid()) _operationQueue->remove(operation);
}

void OperationThread::remove(const std::string& name)
{
    OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_threadMutex);
    if (_operationQueue.valid()) _operationQueue->remove(name);
}

void OperationThread::removeAllOperations()
{
    OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_threadMutex);
    if (_operationQueue.valid()) _operationQueue->removeAllOperations();
}

void OperationThread::run()
{
    OSG_INFO<<"Doing run "<<this<<" isRunning()="<<isRunning()<<std::endl;

    bool firstTime = true;

    do
    {
        // OSG_NOTICE<<"In thread loop "<<this<<std::endl;
        ref_ptr<Operation> operation;
        ref_ptr<OperationQueue> operationQueue;

        {
            OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_threadMutex);
            operationQueue = _operationQueue;
        }

        operation = operationQueue->getNextOperation(true);

        if (_done) break;

        if (operation.valid())
        {
            {
                OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_threadMutex);
                _currentOperation = operation;
            }

            // OSG_INFO<<"Doing op "<<_currentOperation->getName()<<" "<<this<<std::endl;

            // call the graphics operation.
            (*operation)(_parent.get());

            {
                OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_threadMutex);
                _currentOperation = 0;
            }
        }

        if (firstTime)
        {
            // do a yield to get round a peculiar thread hang when testCancel() is called
            // in certain circumstances - of which there is no particular pattern.
            YieldCurrentThread();
            firstTime = false;
        }

        // OSG_NOTICE<<"operations.size()="<<_operations.size()<<" done="<<_done<<" testCancel()"<<testCancel()<<std::endl;

    } while (!testCancel() && !_done);

    OSG_INFO<<"exit loop "<<this<<" isRunning()="<<isRunning()<<std::endl;

}
<commit_msg>Reinstated the original mechanism for OperationThread::cancel() that co-operatively releases blocks/barriers to make sure the thread is able to exit correctly.<commit_after>/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
 *
 * This library is open source and may be redistributed and/or modified under
 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
 * (at your option) any later version.  The full license is in LICENSE file
 * included with this distribution, and on the openscenegraph.org website.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * OpenSceneGraph Public License for more details.
*/

#include <osg/OperationThread>
#include <osg/GraphicsContext>
#include <osg/Notify>

using namespace osg;
using namespace OpenThreads;

struct BlockOperation : public Operation, public Block
{
    BlockOperation():
        Operation("Block",false)
    {
        reset();
    }

    virtual void release()
    {
        Block::release();
    }

    virtual void operator () (Object*)
    {
        glFlush();
        Block::release();
    }
};

/////////////////////////////////////////////////////////////////////////////
//
//  OperationsQueue
//

OperationQueue::OperationQueue():
    osg::Referenced(true)
{
    _currentOperationIterator = _operations.begin();
    _operationsBlock = new RefBlock;
}

OperationQueue::~OperationQueue()
{
}

bool OperationQueue::empty()
{

  OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_operationsMutex);
  return _operations.empty();
}

unsigned int OperationQueue::getNumOperationsInQueue()
{
  OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_operationsMutex);
  return static_cast<unsigned int>(_operations.size());
}

ref_ptr<Operation> OperationQueue::getNextOperation(bool blockIfEmpty)
{
    if (blockIfEmpty && _operations.empty())
    {
        _operationsBlock->block();
    }

    OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_operationsMutex);

    if (_operations.empty()) return osg::ref_ptr<Operation>();

    if (_currentOperationIterator == _operations.end())
    {
        // iterator at end of operations so reset to beginning.
        _currentOperationIterator = _operations.begin();
    }

    ref_ptr<Operation> currentOperation = *_currentOperationIterator;

    if (!currentOperation->getKeep())
    {
        // OSG_INFO<<"removing "<<currentOperation->getName()<<std::endl;

        // remove it from the operations queue
        _currentOperationIterator = _operations.erase(_currentOperationIterator);

        // OSG_INFO<<"size "<<_operations.size()<<std::endl;

        if (_operations.empty())
        {
           // OSG_INFO<<"setting block "<<_operations.size()<<std::endl;
           _operationsBlock->set(false);
        }
    }
    else
    {
        // OSG_INFO<<"increment "<<_currentOperation->getName()<<std::endl;

        // move on to the next operation in the list.
        ++_currentOperationIterator;
    }

    return currentOperation;
}

void OperationQueue::add(Operation* operation)
{
    OSG_INFO<<"Doing add"<<std::endl;

    // acquire the lock on the operations queue to prevent anyone else for modifying it at the same time
    OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_operationsMutex);

    // add the operation to the end of the list
    _operations.push_back(operation);

    _operationsBlock->set(true);
}

void OperationQueue::remove(Operation* operation)
{
    OSG_INFO<<"Doing remove operation"<<std::endl;

    // acquire the lock on the operations queue to prevent anyone else for modifying it at the same time
    OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_operationsMutex);

    for(Operations::iterator itr = _operations.begin();
        itr!=_operations.end();)
    {
        if ((*itr)==operation)
        {
            bool needToResetCurrentIterator = (_currentOperationIterator == itr);

            itr = _operations.erase(itr);

            if (needToResetCurrentIterator) _currentOperationIterator = itr;

        }
        else ++itr;
    }
}

void OperationQueue::remove(const std::string& name)
{
    OSG_INFO<<"Doing remove named operation"<<std::endl;

    // acquire the lock on the operations queue to prevent anyone else for modifying it at the same time
    OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_operationsMutex);

    // find the remove all operations with specified name
    for(Operations::iterator itr = _operations.begin();
        itr!=_operations.end();)
    {
        if ((*itr)->getName()==name)
        {
            bool needToResetCurrentIterator = (_currentOperationIterator == itr);

            itr = _operations.erase(itr);

            if (needToResetCurrentIterator) _currentOperationIterator = itr;
        }
        else ++itr;
    }

    if (_operations.empty())
    {
        _operationsBlock->set(false);
    }
}

void OperationQueue::removeAllOperations()
{
    OSG_INFO<<"Doing remove all operations"<<std::endl;

    OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_operationsMutex);

    _operations.clear();

    // reset current operator.
    _currentOperationIterator = _operations.begin();

    if (_operations.empty())
    {
        _operationsBlock->set(false);
    }
}

void OperationQueue::runOperations(Object* callingObject)
{
    OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_operationsMutex);

    // reset current operation iterator to beginning if at end.
    if (_currentOperationIterator==_operations.end()) _currentOperationIterator = _operations.begin();

    for(;
        _currentOperationIterator != _operations.end();
        )
    {
        ref_ptr<Operation> operation = *_currentOperationIterator;

        if (!operation->getKeep())
        {
            _currentOperationIterator = _operations.erase(_currentOperationIterator);
        }
        else
        {
            ++_currentOperationIterator;
        }

        // OSG_INFO<<"Doing op "<<_currentOperation->getName()<<" "<<this<<std::endl;

        // call the graphics operation.
        (*operation)(callingObject);
    }

    if (_operations.empty())
    {
        _operationsBlock->set(false);
    }
}

void OperationQueue::releaseOperationsBlock()
{
    _operationsBlock->release();
}

 void OperationQueue::releaseAllOperations()
{
    OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_operationsMutex);

    for(Operations::iterator itr = _operations.begin();
        itr!=_operations.end();
        ++itr)
    {
        (*itr)->release();
    }
}

void OperationQueue::addOperationThread(OperationThread* thread)
{
    _operationThreads.insert(thread);
}

void OperationQueue::removeOperationThread(OperationThread* thread)
{
    _operationThreads.erase(thread);
}

/////////////////////////////////////////////////////////////////////////////
//
//  OperationThread
//

OperationThread::OperationThread():
    osg::Referenced(true),
    _parent(0),
    _done(0)
{
    setOperationQueue(new OperationQueue);
}

OperationThread::~OperationThread()
{
    //OSG_NOTICE<<"Destructing graphics thread "<<this<<std::endl;

    cancel();

    //OSG_NOTICE<<"Done Destructing graphics thread "<<this<<std::endl;
}

void OperationThread::setOperationQueue(OperationQueue* opq)
{
    OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_threadMutex);

    if (_operationQueue == opq) return;

    if (_operationQueue.valid()) _operationQueue->removeOperationThread(this);

    _operationQueue = opq;

    if (_operationQueue.valid()) _operationQueue->addOperationThread(this);
}

void OperationThread::setDone(bool done)
{
    unsigned d = done?0:1;
    if (_done==d) return;

    _done.exchange(d);

    if (done)
    {
        OSG_INFO<<"set done "<<this<<std::endl;

        {
            OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_threadMutex);
            if (_currentOperation.valid())
            {
                OSG_INFO<<"releasing "<<_currentOperation.get()<<std::endl;
                _currentOperation->release();
            }
        }

        if (_operationQueue.valid()) _operationQueue->releaseOperationsBlock();
    }
}

int OperationThread::cancel()
{
    OSG_INFO<<"Cancelling OperationThread "<<this<<" isRunning()="<<isRunning()<<std::endl;

    int result = 0;
    if( isRunning() )
    {

        _done.exchange(1);

        OSG_INFO<<"   Doing cancel "<<this<<std::endl;

        {
            OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_threadMutex);

            if (_operationQueue.valid())
            {
                 _operationQueue->releaseOperationsBlock();
                //_operationQueue->releaseAllOperations();
            }

            if (_currentOperation.valid()) _currentOperation->release();
        }

        // then wait for the the thread to stop running.
        while(isRunning())
        {

#if 1
            {
                OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_threadMutex);

                if (_operationQueue.valid())
                {
                    _operationQueue->releaseOperationsBlock();
                    // _operationQueue->releaseAllOperations();
                }

                if (_currentOperation.valid()) _currentOperation->release();
            }
#endif
            // commenting out debug info as it was cashing crash on exit, presumable
            // due to OSG_NOTIFY or std::cout destructing earlier than this destructor.
            OSG_DEBUG<<"   Waiting for OperationThread to cancel "<<this<<std::endl;
            OpenThreads::Thread::YieldCurrentThread();
        }

        // use join to appease valgrind as the above loop won't exit till the thread stops running
        // but valgrind doesn't reconginze this and assumes the thread is still running
        join();
    }

    OSG_INFO<<"  OperationThread::cancel() thread cancelled "<<this<<" isRunning()="<<isRunning()<<std::endl;

    return result;
}

void OperationThread::add(Operation* operation)
{
    OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_threadMutex);
    if (!_operationQueue) _operationQueue = new OperationQueue;
    _operationQueue->add(operation);
}

void OperationThread::remove(Operation* operation)
{
    OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_threadMutex);
    if (_operationQueue.valid()) _operationQueue->remove(operation);
}

void OperationThread::remove(const std::string& name)
{
    OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_threadMutex);
    if (_operationQueue.valid()) _operationQueue->remove(name);
}

void OperationThread::removeAllOperations()
{
    OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_threadMutex);
    if (_operationQueue.valid()) _operationQueue->removeAllOperations();
}

void OperationThread::run()
{
    OSG_INFO<<"Doing run "<<this<<" isRunning()="<<isRunning()<<std::endl;

    bool firstTime = true;

    do
    {
        // OSG_NOTICE<<"In thread loop "<<this<<std::endl;
        ref_ptr<Operation> operation;
        ref_ptr<OperationQueue> operationQueue;

        {
            OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_threadMutex);
            operationQueue = _operationQueue;
        }

        operation = operationQueue->getNextOperation(true);

        if (_done) break;

        if (operation.valid())
        {
            {
                OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_threadMutex);
                _currentOperation = operation;
            }

            // OSG_INFO<<"Doing op "<<_currentOperation->getName()<<" "<<this<<std::endl;

            // call the graphics operation.
            (*operation)(_parent.get());

            {
                OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_threadMutex);
                _currentOperation = 0;
            }
        }

        if (firstTime)
        {
            // do a yield to get round a peculiar thread hang when testCancel() is called
            // in certain circumstances - of which there is no particular pattern.
            YieldCurrentThread();
            firstTime = false;
        }

        // OSG_NOTICE<<"operations.size()="<<_operations.size()<<" done="<<_done<<" testCancel()"<<testCancel()<<std::endl;

    } while (!testCancel() && !_done);

    OSG_INFO<<"exit loop "<<this<<" isRunning()="<<isRunning()<<std::endl;

}
<|endoftext|>


<commit_before>#include "auth-field-test.h"
#include <QSettings>
#include <QtTest>
#include "auth/auth-const-field.h"
#include "auth/auth-field.h"
#include "auth/auth-hash-field.h"
#include "mixed-settings.h"

MixedSettings *makeSettings(QString key, QString value)
{
	QSettings *settings = new QSettings("tests/resources/settings.ini", QSettings::IniFormat);
	settings->setValue(key, value);

	return new MixedSettings(QList<QSettings*>() << settings);
}

void AuthFieldTest::testBasic()
{
	AuthField field("id", "key", AuthField::FieldType::Text);

	QCOMPARE(field.id(), QString("id"));
	QCOMPARE(field.key(), QString("key"));
	QCOMPARE(field.type(), AuthField::FieldType::Text);

	MixedSettings *settings = makeSettings("auth/key", "user");
	QCOMPARE(field.value(settings), QString("user"));
	settings->deleteLater();
}

void AuthFieldTest::testConst()
{
	AuthConstField field("key", "val");

	QCOMPARE(field.key(), QString("key"));
	QCOMPARE(field.type(), AuthField::FieldType::Const);

	MixedSettings *settings = new MixedSettings(QList<QSettings*>());
	QCOMPARE(field.value(settings), QString("val"));
	settings->deleteLater();
}

void AuthFieldTest::testHash()
{
	AuthHashField field("key", QCryptographicHash::Algorithm::Md5, "test-%pseudo%");

	QCOMPARE(field.key(), QString("key"));
	QCOMPARE(field.type(), AuthField::FieldType::Hash);
	QCOMPARE(field.salt(), QString("test-%pseudo%"));

	MixedSettings *settings = makeSettings("auth/pseudo", "user");
	QCOMPARE(field.value(settings), QString("42b27efc1480b4fe6d7eaa5eec47424d")); // md5("test-user")
	settings->deleteLater();
}

QTEST_MAIN(AuthFieldTest)
<commit_msg>Fix AuthFieldTest<commit_after>#include "auth-field-test.h"
#include <QSettings>
#include <QtTest>
#include "auth/auth-const-field.h"
#include "auth/auth-field.h"
#include "auth/auth-hash-field.h"
#include "mixed-settings.h"

MixedSettings *makeSettings(QString key, QString value)
{
	QSettings *settings = new QSettings("tests/resources/settings.ini", QSettings::IniFormat);
	settings->setValue(key, value);

	return new MixedSettings(QList<QSettings*>() << settings);
}

void AuthFieldTest::testBasic()
{
	AuthField field("id", "key", AuthField::FieldType::Text);

	QCOMPARE(field.id(), QString("id"));
	QCOMPARE(field.key(), QString("key"));
	QCOMPARE(field.type(), AuthField::FieldType::Text);

	MixedSettings *settings = makeSettings("auth/id", "user");
	QCOMPARE(field.value(settings), QString("user"));
	settings->deleteLater();
}

void AuthFieldTest::testConst()
{
	AuthConstField field("key", "val");

	QCOMPARE(field.key(), QString("key"));
	QCOMPARE(field.type(), AuthField::FieldType::Const);

	MixedSettings *settings = new MixedSettings(QList<QSettings*>());
	QCOMPARE(field.value(settings), QString("val"));
	settings->deleteLater();
}

void AuthFieldTest::testHash()
{
	AuthHashField field("key", QCryptographicHash::Algorithm::Md5, "test-%pseudo%");

	QCOMPARE(field.key(), QString("key"));
	QCOMPARE(field.type(), AuthField::FieldType::Hash);
	QCOMPARE(field.salt(), QString("test-%pseudo%"));

	MixedSettings *settings = makeSettings("auth/pseudo", "user");
	QCOMPARE(field.value(settings), QString("42b27efc1480b4fe6d7eaa5eec47424d")); // md5("test-user")
	settings->deleteLater();
}

QTEST_MAIN(AuthFieldTest)
<|endoftext|>