<commit_before>// Copyright 2013-2015 Stanford University
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <cmath>

#include <algorithm>
#include <array>
#include <limits>

#include "src/cost/correctness.h"
#include "src/ext/x64asm/include/x64asm.h"

using namespace cpputil;
using namespace std;
using namespace x64asm;

namespace stoke {

CorrectnessCost& CorrectnessCost::set_target(const Cfg& target, bool stack_out, bool heap_out) {
  assert(sandbox_ != nullptr);

  live_out_ = target.live_outs();
  stack_out_ = stack_out;
  heap_out_ = heap_out;

  reference_out_.clear();
  recompute_target_defs(target.live_outs());

  sandbox_->insert_function(target);
  sandbox_->set_entrypoint(target.get_code()[0].get_operand<x64asm::Label>(0));
  sandbox_->run();
  for (auto i = sandbox_->result_begin(), ie = sandbox_->result_end(); i != ie; ++i) {
    reference_out_.push_back(*i);
  }
  return *this;
}

void CorrectnessCost::recompute_target_defs(const RegSet& rs) {

  target_gp_out_.clear();
  for (auto i = rs.gp_begin(), ie = rs.gp_end(); i != ie; ++i) {
    target_gp_out_.push_back(*i);
  }

  target_sse_out_.clear();
  for (auto i = rs.any_sub_sse_begin(), ie = rs.any_sub_sse_end(); i != ie; ++i) {
    target_sse_out_.push_back(*i);
  }

  // TODO -- An x64asm iterator over these flags would be nice
  target_rf_out_.clear();
  for (auto f: {
         eflags_cf, eflags_pf, eflags_af, eflags_zf, eflags_of, eflags_sf
       }) {
    if (rs.contains(f)) {
      target_rf_out_.push_back(f);
    }
  }
}

/** Evaluate a rewrite. This method may shortcircuit and return max as soon as its
  result would equal or exceed that value. */
CorrectnessCost::result_type CorrectnessCost::operator()(const Cfg& cfg, const Cost max) {

  run_sandbox(cfg);

  auto cost = evaluate_correctness(cfg, max);
  bool correct = cost == 0;
  return result_type(correct, cost);
}

Cost CorrectnessCost::evaluate_correctness(const Cfg& cfg, const Cost max) {

  switch (reduction_) {
  case Reduction::MAX:
    return max_correctness(cfg, max);
  case Reduction::SUM:
    return sum_correctness(cfg, max);
  default:
    assert(false);
    return 0;
  }
}

Cost CorrectnessCost::max_correctness(const Cfg& cfg, const Cost max) {
  Cost res = 0;
  counter_example_testcase_ = -1;

  size_t i = 0;
  for (size_t ie = sandbox_->size(); res < max && i < ie; ++i) {
    const auto err = evaluate_error(reference_out_[i], *(sandbox_->get_result(i)), cfg.def_outs());
    assert(err <= max_testcase_cost);
    if (err != 0 && counter_example_testcase_ < 0) {
      counter_example_testcase_ = i;
    }

    res = std::max(res, err);
  }

  assert(res <= max_correctness_cost);
  return res;
}

Cost CorrectnessCost::sum_correctness(const Cfg& cfg, const Cost max) {
  Cost res = 0;
  counter_example_testcase_ = -1;

  size_t i = 0;
  for (size_t ie = sandbox_->size(); res < max && i < ie; ++i) {
    const auto err = evaluate_error(reference_out_[i], *(sandbox_->get_result(i)), cfg.def_outs());
    assert(err <= max_testcase_cost);
    if (err != 0 && counter_example_testcase_ < 0) {
      counter_example_testcase_ = i;
    }

    res += err;
  }

  assert(res <= max_correctness_cost);
  return res;
}

Cost CorrectnessCost::evaluate_error(const CpuState& t, const CpuState& r, const RegSet& defs) const {
  // Only assess a signal penalty if target and rewrite disagree
  if (t.code != r.code) {
    return sig_penalty_;
  }
  // If this testcase has signalled, we can't guarantee register state --
  // and technically we can do whatever we want with signalling code -- so
  // just return zero cost
  if (t.code != ErrorCode::NORMAL) {
    return 0;
  }

  // Otherwise, we can do the usual thing and check results register by register
  Cost cost = 0;
  cost += gp_error(t, r, defs);
  cost += sse_error(t.sse, r.sse, defs);
  cost += rflags_error(t.rf, r.rf, defs);
  if (stack_out_) {
    cost += mem_error(t.stack, r.stack);
  }
  if (heap_out_) {
    cost += block_heap_ ? block_mem_error(t.heap, r.heap, r.sse, defs) : mem_error(t.heap, r.heap);
  }

  return cost;
}

Cost CorrectnessCost::gp_error(const CpuState& t, const CpuState& r, const RegSet& defs) const {
  Cost cost = 0;

  for (const auto& r_t : target_gp_out_) {
    auto size = r_t.size();
    auto bytes = size/8;
    auto delta = undef_default(bytes);
    const auto val_t = t[r_t];
    auto is_t_rh = (r_t).type() == Type::RH;

    for (auto r_r = defs.gp_begin(), r_re = defs.gp_end(); r_r != r_re; ++r_r) {
      if (r_t != *r_r && !relax_reg_) {
        continue;
      }
      if ((*r_r).size() < size) {
        continue;
      }

      uint64_t val_r;
      bool is_same = false;
      auto is_r_rh = (*r_r).type() == Type::RH;
      if (!(is_t_rh && is_r_rh)) {
        // normal case, we are looking at two non-rh registers
        val_r = r.read_gp(*r_r, size, 0);
        is_same = ((uint64_t)r_t) == ((uint64_t)*r_r);
      } else if (is_t_rh && is_r_rh) {
        // we are comparing two rh registers, also simple
        val_r = r[*r_r];
        is_same = r_t == *r_r;
      } else if (is_t_rh) {
        // t is an rh register, but r is not:

        // make sure that there is a corresponding rh register, and that it's defined
        if ((*r_r).size() < 16 || *r_r >= 4) {
          continue;
        }
        // get rh register that corresponds to r
        auto rh = Constants::rhs()[*r_r];
        is_same = false;
        val_r = r[rh];
      } else {
        // r is an rh register, but t is not, so no match
        assert(is_r_rh);
        continue;
      }

      const auto eval = evaluate_distance(val_t, val_r) + (is_same ? 0 : misalign_penalty_);

      delta = min(delta, eval);
    }
    cost += delta;
  }

  return cost;
}

Cost CorrectnessCost::sse_error(const Regs& t, const Regs& r, const RegSet& defs) const {
  Cost cost = 0;

  for (size_t i = 0; i < sse_count_; ++i) {
    for (const auto& s_t : target_sse_out_) {
      auto delta = undef_default(sse_width_);
      uint64_t val_t = 0;
      switch (sse_width_) {
      case 1:
        val_t = t[s_t].get_fixed_byte(i);
        break;
      case 2:
        val_t = t[s_t].get_fixed_word(i);
        break;
      case 4:
        val_t = t[s_t].get_fixed_double(i);
        break;
      case 8:
        val_t = t[s_t].get_fixed_quad(i);
        break;
      default:
        assert(false);
        break;
      }

      for (auto s_r = defs.any_sub_sse_begin(), s_re = defs.any_sub_sse_end(); s_r != s_re; ++s_r) {
        if (s_t != *s_r && !relax_reg_) {
          continue;
        }

        uint64_t val_r = 0;
        switch (sse_width_) {
        case 1:
          val_r = r[*s_r].get_fixed_byte(i);
          break;
        case 2:
          val_r = r[*s_r].get_fixed_word(i);
          break;
        case 4:
          val_r = r[*s_r].get_fixed_double(i);
          break;
        case 8:
          val_r = r[*s_r].get_fixed_quad(i);
          break;
        default:
          assert(false);
          break;
        }

        const auto eval = evaluate_distance(val_t, val_r) + ((s_t == *s_r) ? 0 : misalign_penalty_);
        delta = min(delta, eval);
      }
      cost += delta;
    }
  }

  return cost;
}

Cost CorrectnessCost::mem_error(const Memory& t, const Memory& r) const {
  Cost cost = 0;

  for (auto i = t.valid_begin(), ie = t.valid_end(); i != ie; ++i) {
    if (relax_mem_) {
      Cost delta = undef_default(1);
      for (auto j = r.valid_begin(), je = r.valid_end(); j != je; ++j) {
        const auto eval = evaluate_distance(t[*i], r[*j]) + (*i == *j ? 0 : misalign_penalty_);
        delta = min(delta, eval);
      }
      cost += delta;
    } else {
      cost += evaluate_distance(t[*i], r[*i]);
    }
  }

  return cost;
}

Cost CorrectnessCost::block_mem_error(const Memory& t, const Memory& rmem, const Regs& rsse, const RegSet& defs) const {
  Cost cost = 0;

  for (size_t i = *t.valid_begin(), ie = t.upper_bound(); i < ie; i += 16) {
    // Skip invalid blocks
    if (!t.is_valid(i)) {
      assert(!t.is_valid_quad(i));
      assert(!t.is_valid_quad(i+8));
      continue;
    }
    // Make sure that this block is valid in the target and rewrite
    assert(t.is_valid_quad(i) && rmem.is_valid_quad(i));
    assert(t.is_valid_quad(i+8) && rmem.is_valid_quad(i+8));

    // Start off with vanilla memory to memory comparison
    Cost delta = evaluate_distance(t.get_quad(i), rmem.get_quad(i)) +
                 evaluate_distance(t.get_quad(i+8), rmem.get_quad(i+8));

    // If we've relaxed mem, we can also look in sse registers
    if (relax_mem_) {
      for (auto s_r = defs.any_sub_sse_begin(), s_re = defs.any_sub_sse_end(); s_r != s_re; ++s_r) {
        Cost eval = evaluate_distance(t.get_quad(i), rsse[*s_r].get_fixed_quad(0)) +
                    evaluate_distance(t.get_quad(i+8), rsse[*s_r].get_fixed_quad(1)) +
                    misalign_penalty_;
        delta = min(delta, eval);
      }
    }

    // Now accrue the lowest cost we were able to find
    cost += delta;
  }

  return cost;
}

Cost CorrectnessCost::rflags_error(const RFlags& t, const RFlags& r, const RegSet& defs) const {
  Cost cost = 0;

  for (auto f : target_rf_out_) {
    const auto i = f.index();
    cost += defs.contains(f) ? (t.is_set(i) ^ r.is_set(i)) : 1;
  }

  return cost;
}

Cost CorrectnessCost::undef_default(size_t num_bytes) const {
  Cost res = 0;
  switch (distance_) {
  case Distance::HAMMING:
    res = 8*num_bytes;
    break;
  case Distance::DOUBLEWORD:
    res = (num_bytes + 3)/4;
    break;
  case Distance::ULP:
    res = max_error_cost;
    break;
  case Distance::EXTENSION:
    // Add user-defined implementation here ...
    res = 0;
    break;

  default:
    assert(false);
    res = 0;
  }

  // Invariant 1: Penalty should not exceed max_error_cost
  assert(res <= max_error_cost);
  return res;
}

Cost CorrectnessCost::evaluate_distance(uint64_t x, uint64_t y) const {
  switch (distance_) {
  case Distance::HAMMING:
    return hamming_distance(x, y);
  case Distance::ULP:
    return ulp_distance(x, y);
  case Distance::DOUBLEWORD:
    return doubleword_distance(x,y);
  default:
    assert(false);
    return 0;
  }
}

Cost CorrectnessCost::ulp_distance(uint64_t x, uint64_t y) const {
  auto t = *((int64_t*)&x);
  t = t < 0 ? numeric_limits<int64_t>::min() - t : t;

  auto r = *((int64_t*)&y);
  r = r < 0 ? numeric_limits<int64_t>::min() - r : r;

  uint64_t ulp = t >= r ? t - r : r - t;
  ulp = ulp < min_ulp_ ? 0 : ulp - min_ulp_;

  return ulp > max_error_cost ? max_error_cost : ulp;
}

} // namespace stoke
<commit_msg>undo invalid fix<commit_after>// Copyright 2013-2015 Stanford University
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <cmath>

#include <algorithm>
#include <array>
#include <limits>

#include "src/cost/correctness.h"
#include "src/ext/x64asm/include/x64asm.h"

using namespace cpputil;
using namespace std;
using namespace x64asm;

namespace stoke {

CorrectnessCost& CorrectnessCost::set_target(const Cfg& target, bool stack_out, bool heap_out) {
  assert(sandbox_ != nullptr);

  live_out_ = target.live_outs();
  stack_out_ = stack_out;
  heap_out_ = heap_out;

  reference_out_.clear();
  recompute_target_defs(target.live_outs());

  sandbox_->insert_function(target);
  sandbox_->set_entrypoint(target.get_code()[0].get_operand<x64asm::Label>(0));
  sandbox_->run();
  for (auto i = sandbox_->result_begin(), ie = sandbox_->result_end(); i != ie; ++i) {
    reference_out_.push_back(*i);
  }
  return *this;
}

void CorrectnessCost::recompute_target_defs(const RegSet& rs) {

  target_gp_out_.clear();
  for (auto i = rs.gp_begin(), ie = rs.gp_end(); i != ie; ++i) {
    target_gp_out_.push_back(*i);
  }

  target_sse_out_.clear();
  for (auto i = rs.any_sub_sse_begin(), ie = rs.any_sub_sse_end(); i != ie; ++i) {
    target_sse_out_.push_back(*i);
  }

  // TODO -- An x64asm iterator over these flags would be nice
  target_rf_out_.clear();
  for (auto f: {
         eflags_cf, eflags_pf, eflags_af, eflags_zf, eflags_of, eflags_sf
       }) {
    if (rs.contains(f)) {
      target_rf_out_.push_back(f);
    }
  }
}

/** Evaluate a rewrite. This method may shortcircuit and return max as soon as its
  result would equal or exceed that value. */
CorrectnessCost::result_type CorrectnessCost::operator()(const Cfg& cfg, const Cost max) {

  run_sandbox(cfg);

  auto cost = evaluate_correctness(cfg, max);
  bool correct = cost == 0;
  return result_type(correct, cost);
}

Cost CorrectnessCost::evaluate_correctness(const Cfg& cfg, const Cost max) {

  switch (reduction_) {
  case Reduction::MAX:
    return max_correctness(cfg, max);
  case Reduction::SUM:
    return sum_correctness(cfg, max);
  default:
    assert(false);
    return 0;
  }
}

Cost CorrectnessCost::max_correctness(const Cfg& cfg, const Cost max) {
  Cost res = 0;
  counter_example_testcase_ = -1;

  size_t i = 0;
  for (size_t ie = sandbox_->size(); res < max && i < ie; ++i) {
    const auto err = evaluate_error(reference_out_[i], *(sandbox_->get_result(i)), cfg.def_outs());
    assert(err <= max_testcase_cost);
    if (err != 0 && counter_example_testcase_ < 0) {
      counter_example_testcase_ = i;
    }

    res = std::max(res, err);
  }

  assert(res <= max_correctness_cost);
  return res;
}

Cost CorrectnessCost::sum_correctness(const Cfg& cfg, const Cost max) {
  Cost res = 0;
  counter_example_testcase_ = -1;

  size_t i = 0;
  for (size_t ie = sandbox_->size(); res < max && i < ie; ++i) {
    const auto err = evaluate_error(reference_out_[i], *(sandbox_->get_result(i)), cfg.def_outs());
    assert(err <= max_testcase_cost);
    if (err != 0 && counter_example_testcase_ < 0) {
      counter_example_testcase_ = i;
    }

    res += err;
  }

  assert(res <= max_correctness_cost);
  return res;
}

Cost CorrectnessCost::evaluate_error(const CpuState& t, const CpuState& r, const RegSet& defs) const {
  // Only assess a signal penalty if target and rewrite disagree
  if (t.code != r.code) {
    return sig_penalty_;
  }
  // If this testcase has signalled, we can't guarantee register state --
  // and technically we can do whatever we want with signalling code -- so
  // just return zero cost
  if (t.code != ErrorCode::NORMAL) {
    return 0;
  }

  // Otherwise, we can do the usual thing and check results register by register
  Cost cost = 0;
  cost += gp_error(t, r, defs);
  cost += sse_error(t.sse, r.sse, defs);
  cost += rflags_error(t.rf, r.rf, defs);
  if (stack_out_) {
    cost += mem_error(t.stack, r.stack);
  }
  if (heap_out_) {
    cost += block_heap_ ? block_mem_error(t.heap, r.heap, r.sse, defs) : mem_error(t.heap, r.heap);
  }

  return cost;
}

Cost CorrectnessCost::gp_error(const CpuState& t, const CpuState& r, const RegSet& defs) const {
  Cost cost = 0;

  for (const auto& r_t : target_gp_out_) {
    auto size = r_t.size();
    auto bytes = size/8;
    auto delta = undef_default(bytes);
    const auto val_t = t[r_t];
    auto is_t_rh = (r_t).type() == Type::RH;

    for (auto r_r = defs.gp_begin(), r_re = defs.gp_end(); r_r != r_re; ++r_r) {
      if (r_t != *r_r && !relax_reg_) {
        continue;
      }
      if ((*r_r).size() < size) {
        continue;
      }

      uint64_t val_r;
      bool is_same = false;
      auto is_r_rh = (*r_r).type() == Type::RH;
      if (!(is_t_rh && is_r_rh)) {
        // normal case, we are looking at two non-rh registers
        val_r = r.read_gp(*r_r, size, 0);
        is_same = ((uint64_t)r_t) == ((uint64_t)*r_r);
      } else if (is_t_rh && is_r_rh) {
        // we are comparing two rh registers, also simple
        val_r = r[*r_r];
        is_same = r_t == *r_r;
      } else if (is_t_rh) {
        // t is an rh register, but r is not:

        // make sure that there is a corresponding rh register, and that it's defined
        if ((*r_r).size() < 16 || *r_r >= 4) {
          continue;
        }
        // get rh register that corresponds to r
        auto rh = Constants::rhs()[*r_r];
        is_same = rh == r_t;
        val_r = r[rh];
      } else {
        // r is an rh register, but t is not, so no match
        assert(is_r_rh);
        continue;
      }

      const auto eval = evaluate_distance(val_t, val_r) + (is_same ? 0 : misalign_penalty_);

      delta = min(delta, eval);
    }
    cost += delta;
  }

  return cost;
}

Cost CorrectnessCost::sse_error(const Regs& t, const Regs& r, const RegSet& defs) const {
  Cost cost = 0;

  for (size_t i = 0; i < sse_count_; ++i) {
    for (const auto& s_t : target_sse_out_) {
      auto delta = undef_default(sse_width_);
      uint64_t val_t = 0;
      switch (sse_width_) {
      case 1:
        val_t = t[s_t].get_fixed_byte(i);
        break;
      case 2:
        val_t = t[s_t].get_fixed_word(i);
        break;
      case 4:
        val_t = t[s_t].get_fixed_double(i);
        break;
      case 8:
        val_t = t[s_t].get_fixed_quad(i);
        break;
      default:
        assert(false);
        break;
      }

      for (auto s_r = defs.any_sub_sse_begin(), s_re = defs.any_sub_sse_end(); s_r != s_re; ++s_r) {
        if (s_t != *s_r && !relax_reg_) {
          continue;
        }

        uint64_t val_r = 0;
        switch (sse_width_) {
        case 1:
          val_r = r[*s_r].get_fixed_byte(i);
          break;
        case 2:
          val_r = r[*s_r].get_fixed_word(i);
          break;
        case 4:
          val_r = r[*s_r].get_fixed_double(i);
          break;
        case 8:
          val_r = r[*s_r].get_fixed_quad(i);
          break;
        default:
          assert(false);
          break;
        }

        const auto eval = evaluate_distance(val_t, val_r) + ((s_t == *s_r) ? 0 : misalign_penalty_);
        delta = min(delta, eval);
      }
      cost += delta;
    }
  }

  return cost;
}

Cost CorrectnessCost::mem_error(const Memory& t, const Memory& r) const {
  Cost cost = 0;

  for (auto i = t.valid_begin(), ie = t.valid_end(); i != ie; ++i) {
    if (relax_mem_) {
      Cost delta = undef_default(1);
      for (auto j = r.valid_begin(), je = r.valid_end(); j != je; ++j) {
        const auto eval = evaluate_distance(t[*i], r[*j]) + (*i == *j ? 0 : misalign_penalty_);
        delta = min(delta, eval);
      }
      cost += delta;
    } else {
      cost += evaluate_distance(t[*i], r[*i]);
    }
  }

  return cost;
}

Cost CorrectnessCost::block_mem_error(const Memory& t, const Memory& rmem, const Regs& rsse, const RegSet& defs) const {
  Cost cost = 0;

  for (size_t i = *t.valid_begin(), ie = t.upper_bound(); i < ie; i += 16) {
    // Skip invalid blocks
    if (!t.is_valid(i)) {
      assert(!t.is_valid_quad(i));
      assert(!t.is_valid_quad(i+8));
      continue;
    }
    // Make sure that this block is valid in the target and rewrite
    assert(t.is_valid_quad(i) && rmem.is_valid_quad(i));
    assert(t.is_valid_quad(i+8) && rmem.is_valid_quad(i+8));

    // Start off with vanilla memory to memory comparison
    Cost delta = evaluate_distance(t.get_quad(i), rmem.get_quad(i)) +
                 evaluate_distance(t.get_quad(i+8), rmem.get_quad(i+8));

    // If we've relaxed mem, we can also look in sse registers
    if (relax_mem_) {
      for (auto s_r = defs.any_sub_sse_begin(), s_re = defs.any_sub_sse_end(); s_r != s_re; ++s_r) {
        Cost eval = evaluate_distance(t.get_quad(i), rsse[*s_r].get_fixed_quad(0)) +
                    evaluate_distance(t.get_quad(i+8), rsse[*s_r].get_fixed_quad(1)) +
                    misalign_penalty_;
        delta = min(delta, eval);
      }
    }

    // Now accrue the lowest cost we were able to find
    cost += delta;
  }

  return cost;
}

Cost CorrectnessCost::rflags_error(const RFlags& t, const RFlags& r, const RegSet& defs) const {
  Cost cost = 0;

  for (auto f : target_rf_out_) {
    const auto i = f.index();
    cost += defs.contains(f) ? (t.is_set(i) ^ r.is_set(i)) : 1;
  }

  return cost;
}

Cost CorrectnessCost::undef_default(size_t num_bytes) const {
  Cost res = 0;
  switch (distance_) {
  case Distance::HAMMING:
    res = 8*num_bytes;
    break;
  case Distance::DOUBLEWORD:
    res = (num_bytes + 3)/4;
    break;
  case Distance::ULP:
    res = max_error_cost;
    break;
  case Distance::EXTENSION:
    // Add user-defined implementation here ...
    res = 0;
    break;

  default:
    assert(false);
    res = 0;
  }

  // Invariant 1: Penalty should not exceed max_error_cost
  assert(res <= max_error_cost);
  return res;
}

Cost CorrectnessCost::evaluate_distance(uint64_t x, uint64_t y) const {
  switch (distance_) {
  case Distance::HAMMING:
    return hamming_distance(x, y);
  case Distance::ULP:
    return ulp_distance(x, y);
  case Distance::DOUBLEWORD:
    return doubleword_distance(x,y);
  default:
    assert(false);
    return 0;
  }
}

Cost CorrectnessCost::ulp_distance(uint64_t x, uint64_t y) const {
  auto t = *((int64_t*)&x);
  t = t < 0 ? numeric_limits<int64_t>::min() - t : t;

  auto r = *((int64_t*)&y);
  r = r < 0 ? numeric_limits<int64_t>::min() - r : r;

  uint64_t ulp = t >= r ? t - r : r - t;
  ulp = ulp < min_ulp_ ? 0 : ulp - min_ulp_;

  return ulp > max_error_cost ? max_error_cost : ulp;
}

} // namespace stoke
<|endoftext|>


<commit_before>// Copyright (c) 2016 Jack Grigg
// Copyright (c) 2016 The Zcash developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

// Implementation of the Equihash Proof-of-Work algorithm.
//
// Reference
// =========
// Alex Biryukov and Dmitry Khovratovich
// Equihash: Asymmetric Proof-of-Work Based on the Generalized Birthday Problem
// NDSS ’16, 21-24 February 2016, San Diego, CA, USA
// https://www.internetsociety.org/sites/default/files/blogs-media/equihash-asymmetric-proof-of-work-based-generalized-birthday-problem.pdf

#include "crypto/equihash.h"
#include "util.h"

#include <algorithm>
#include <cmath>
#include <iostream>
#include <stdexcept>

void validate_params(int n, int k)
{
    if (k>=n) {
        std::cerr << "n must be larger than k\n";
        throw invalid_params();
    }
    if (n % 8 != 0) {
        std::cerr << "Parameters must satisfy n = 0 mod 8\n";
        throw invalid_params();
    }
    if ((n/(k+1)) % 8 != 0) {
        std::cerr << "Parameters must satisfy n/(k+1) = 0 mod 8\n";
        throw invalid_params();
    }
}

int Equihash::InitialiseState(eh_HashState& base_state)
{
    unsigned char personalization[crypto_generichash_blake2b_PERSONALBYTES] = {};
    memcpy(personalization, "ZcashPOW", 8);
    memcpy(personalization+8,  &n, 4);
    memcpy(personalization+12, &k, 4);
    return crypto_generichash_blake2b_init_salt_personal(&base_state,
                                                         NULL, 0, // No key.
                                                         n/8,
                                                         NULL,    // No salt.
                                                         personalization);
}

StepRow::StepRow(unsigned int n, const eh_HashState& base_state, eh_index i) :
        hash {new unsigned char[n/8]},
        len {n/8},
        indices {i}
{
    eh_HashState state;
    state = base_state;
    crypto_generichash_blake2b_update(&state, (unsigned char*) &i, sizeof(eh_index));
    crypto_generichash_blake2b_final(&state, hash, n/8);

    assert(indices.size() == 1);
}

StepRow::~StepRow()
{
    delete[] hash;
}

StepRow::StepRow(const StepRow& a) :
        hash {new unsigned char[a.len]},
        len {a.len},
        indices(a.indices)
{
    for (int i = 0; i < len; i++)
        hash[i] = a.hash[i];
}

StepRow& StepRow::operator=(const StepRow& a)
{
    unsigned char* p = new unsigned char[a.len];
    for (int i = 0; i < a.len; i++)
        p[i] = a.hash[i];
    delete[] hash;
    hash = p;
    len = a.len;
    indices = a.indices;
    return *this;
}

StepRow& StepRow::operator^=(const StepRow& a)
{
    if (a.len != len) {
        throw std::invalid_argument("Hash length differs");
    }
    if (a.indices.size() != indices.size()) {
        throw std::invalid_argument("Number of indices differs");
    }
    unsigned char* p = new unsigned char[len];
    for (int i = 0; i < len; i++)
        p[i] = hash[i] ^ a.hash[i];
    delete[] hash;
    hash = p;
    indices.reserve(indices.size() + a.indices.size());
    indices.insert(indices.end(), a.indices.begin(), a.indices.end());
    return *this;
}

void StepRow::TrimHash(int l)
{
    unsigned char* p = new unsigned char[len-l];
    for (int i = 0; i < len-l; i++)
        p[i] = hash[i+l];
    delete[] hash;
    hash = p;
    len -= l;
}

bool StepRow::IsZero()
{
    char res = 0;
    for (int i = 0; i < len; i++)
        res |= hash[i];
    return res == 0;
}

bool HasCollision(StepRow& a, StepRow& b, int l)
{
    bool res = true;
    for (int j = 0; j < l; j++)
        res &= a.hash[j] == b.hash[j];
    return res;
}

// Checks if the intersection of a.indices and b.indices is empty
bool DistinctIndices(const StepRow& a, const StepRow& b)
{
    std::vector<eh_index> aSrt(a.indices);
    std::vector<eh_index> bSrt(b.indices);

    std::sort(aSrt.begin(), aSrt.end());
    std::sort(bSrt.begin(), bSrt.end());

    unsigned int i = 0;
    for (unsigned int j = 0; j < bSrt.size(); j++) {
        while (aSrt[i] < bSrt[j]) {
            i++;
            if (i == aSrt.size()) { return true; }
        }
        assert(aSrt[i] >= bSrt[j]);
        if (aSrt[i] == bSrt[j]) { return false; }
    }
    return true;
}

Equihash::Equihash(unsigned int n, unsigned int k) :
        n(n), k(k)
{
    validate_params(n, k);
}

std::set<std::vector<eh_index>> Equihash::BasicSolve(const eh_HashState& base_state)
{
    assert(CollisionBitLength() + 1 < 8*sizeof(eh_index));
    eh_index init_size { ((eh_index) 1) << (CollisionBitLength() + 1) };

    // 1) Generate first list
    LogPrint("pow", "Generating first list\n");
    std::vector<StepRow> X;
    X.reserve(init_size);
    for (eh_index i = 0; i < init_size; i++) {
        X.emplace_back(n, base_state, i);
    }

    // 3) Repeat step 2 until 2n/(k+1) bits remain
    for (int r = 1; r < k && X.size() > 0; r++) {
        LogPrint("pow", "Round %d:\n", r);
        // 2a) Sort the list
        LogPrint("pow", "- Sorting list\n");
        std::sort(X.begin(), X.end());

        LogPrint("pow", "- Finding collisions\n");
        int i = 0;
        int posFree = 0;
        std::vector<StepRow> Xc;
        while (i < X.size() - 1) {
            // 2b) Find next set of unordered pairs with collisions on the next n/(k+1) bits
            int j = 1;
            while (i+j < X.size() &&
                    HasCollision(X[i], X[i+j], CollisionByteLength())) {
                j++;
            }

            // 2c) Calculate tuples (X_i ^ X_j, (i, j))
            for (int l = 0; l < j - 1; l++) {
                for (int m = l + 1; m < j; m++) {
                    if (DistinctIndices(X[i+l], X[i+m])) {
                        Xc.push_back(X[i+l] ^ X[i+m]);
                        Xc.back().TrimHash(CollisionByteLength());
                    }
                }
            }

            // 2d) Store tuples on the table in-place if possible
            while (posFree < i+j && Xc.size() > 0) {
                X[posFree++] = Xc.back();
                Xc.pop_back();
            }

            i += j;
        }

        // 2e) Handle edge case where final table entry has no collision
        while (posFree < X.size() && Xc.size() > 0) {
            X[posFree++] = Xc.back();
            Xc.pop_back();
        }

        if (Xc.size() > 0) {
            // 2f) Add overflow to end of table
            X.insert(X.end(), Xc.begin(), Xc.end());
        } else if (posFree < X.size()) {
            // 2g) Remove empty space at the end
            X.erase(X.begin()+posFree, X.end());
            X.shrink_to_fit();
        }
    }

    // k+1) Find a collision on last 2n(k+1) bits
    LogPrint("pow", "Final round:\n");
    std::set<std::vector<eh_index>> solns;
    if (X.size() > 1) {
        LogPrint("pow", "- Sorting list\n");
        std::sort(X.begin(), X.end());
        LogPrint("pow", "- Finding collisions\n");
        for (int i = 0; i < X.size() - 1; i++) {
            StepRow res = X[i] ^ X[i+1];
            if (res.IsZero() && DistinctIndices(X[i], X[i+1])) {
                solns.insert(res.GetSolution());
            }
        }
    } else
        LogPrint("pow", "- List is empty\n");

    return solns;
}

bool Equihash::IsValidSolution(const eh_HashState& base_state, std::vector<eh_index> soln)
{
    eh_index soln_size { pow(2, k) };
    if (soln.size() != soln_size) {
        LogPrint("pow", "Invalid solution size: %d\n", soln.size());
        return false;
    }

    std::vector<StepRow> X;
    X.reserve(soln_size);
    for (eh_index i : soln) {
        X.emplace_back(n, base_state, i);
    }

    while (X.size() > 1) {
        std::vector<StepRow> Xc;
        for (int i = 0; i < X.size(); i += 2) {
            if (!HasCollision(X[i], X[i+1], CollisionByteLength())) {
                LogPrint("pow", "Invalid solution: invalid collision length between StepRows\n");
                LogPrint("pow", "X[i]   = %s\n", X[i].GetHex());
                LogPrint("pow", "X[i+1] = %s\n", X[i+1].GetHex());
                return false;
            }
            if (X[i+1].IndicesBefore(X[i])) {
                return false;
                LogPrint("pow", "Invalid solution: Index tree incorrectly ordered\n");
            }
            if (!DistinctIndices(X[i], X[i+1])) {
                LogPrint("pow", "Invalid solution: duplicate indices\n");
                return false;
            }
            Xc.push_back(X[i] ^ X[i+1]);
            Xc.back().TrimHash(CollisionByteLength());
        }
        X = Xc;
    }

    assert(X.size() == 1);
    return X[0].IsZero();
}
<commit_msg>Use left shift instead of floating-point pow() in equihash.<commit_after>// Copyright (c) 2016 Jack Grigg
// Copyright (c) 2016 The Zcash developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

// Implementation of the Equihash Proof-of-Work algorithm.
//
// Reference
// =========
// Alex Biryukov and Dmitry Khovratovich
// Equihash: Asymmetric Proof-of-Work Based on the Generalized Birthday Problem
// NDSS ’16, 21-24 February 2016, San Diego, CA, USA
// https://www.internetsociety.org/sites/default/files/blogs-media/equihash-asymmetric-proof-of-work-based-generalized-birthday-problem.pdf

#include "crypto/equihash.h"
#include "util.h"

#include <algorithm>
#include <iostream>
#include <stdexcept>

void validate_params(int n, int k)
{
    if (k>=n) {
        std::cerr << "n must be larger than k\n";
        throw invalid_params();
    }
    if (n % 8 != 0) {
        std::cerr << "Parameters must satisfy n = 0 mod 8\n";
        throw invalid_params();
    }
    if ((n/(k+1)) % 8 != 0) {
        std::cerr << "Parameters must satisfy n/(k+1) = 0 mod 8\n";
        throw invalid_params();
    }
}

int Equihash::InitialiseState(eh_HashState& base_state)
{
    unsigned char personalization[crypto_generichash_blake2b_PERSONALBYTES] = {};
    memcpy(personalization, "ZcashPOW", 8);
    memcpy(personalization+8,  &n, 4);
    memcpy(personalization+12, &k, 4);
    return crypto_generichash_blake2b_init_salt_personal(&base_state,
                                                         NULL, 0, // No key.
                                                         n/8,
                                                         NULL,    // No salt.
                                                         personalization);
}

StepRow::StepRow(unsigned int n, const eh_HashState& base_state, eh_index i) :
        hash {new unsigned char[n/8]},
        len {n/8},
        indices {i}
{
    eh_HashState state;
    state = base_state;
    crypto_generichash_blake2b_update(&state, (unsigned char*) &i, sizeof(eh_index));
    crypto_generichash_blake2b_final(&state, hash, n/8);

    assert(indices.size() == 1);
}

StepRow::~StepRow()
{
    delete[] hash;
}

StepRow::StepRow(const StepRow& a) :
        hash {new unsigned char[a.len]},
        len {a.len},
        indices(a.indices)
{
    for (int i = 0; i < len; i++)
        hash[i] = a.hash[i];
}

StepRow& StepRow::operator=(const StepRow& a)
{
    unsigned char* p = new unsigned char[a.len];
    for (int i = 0; i < a.len; i++)
        p[i] = a.hash[i];
    delete[] hash;
    hash = p;
    len = a.len;
    indices = a.indices;
    return *this;
}

StepRow& StepRow::operator^=(const StepRow& a)
{
    if (a.len != len) {
        throw std::invalid_argument("Hash length differs");
    }
    if (a.indices.size() != indices.size()) {
        throw std::invalid_argument("Number of indices differs");
    }
    unsigned char* p = new unsigned char[len];
    for (int i = 0; i < len; i++)
        p[i] = hash[i] ^ a.hash[i];
    delete[] hash;
    hash = p;
    indices.reserve(indices.size() + a.indices.size());
    indices.insert(indices.end(), a.indices.begin(), a.indices.end());
    return *this;
}

void StepRow::TrimHash(int l)
{
    unsigned char* p = new unsigned char[len-l];
    for (int i = 0; i < len-l; i++)
        p[i] = hash[i+l];
    delete[] hash;
    hash = p;
    len -= l;
}

bool StepRow::IsZero()
{
    char res = 0;
    for (int i = 0; i < len; i++)
        res |= hash[i];
    return res == 0;
}

bool HasCollision(StepRow& a, StepRow& b, int l)
{
    bool res = true;
    for (int j = 0; j < l; j++)
        res &= a.hash[j] == b.hash[j];
    return res;
}

// Checks if the intersection of a.indices and b.indices is empty
bool DistinctIndices(const StepRow& a, const StepRow& b)
{
    std::vector<eh_index> aSrt(a.indices);
    std::vector<eh_index> bSrt(b.indices);

    std::sort(aSrt.begin(), aSrt.end());
    std::sort(bSrt.begin(), bSrt.end());

    unsigned int i = 0;
    for (unsigned int j = 0; j < bSrt.size(); j++) {
        while (aSrt[i] < bSrt[j]) {
            i++;
            if (i == aSrt.size()) { return true; }
        }
        assert(aSrt[i] >= bSrt[j]);
        if (aSrt[i] == bSrt[j]) { return false; }
    }
    return true;
}

Equihash::Equihash(unsigned int n, unsigned int k) :
        n(n), k(k)
{
    validate_params(n, k);
}

std::set<std::vector<eh_index>> Equihash::BasicSolve(const eh_HashState& base_state)
{
    assert(CollisionBitLength() + 1 < 8*sizeof(eh_index));
    eh_index init_size { ((eh_index) 1) << (CollisionBitLength() + 1) };

    // 1) Generate first list
    LogPrint("pow", "Generating first list\n");
    std::vector<StepRow> X;
    X.reserve(init_size);
    for (eh_index i = 0; i < init_size; i++) {
        X.emplace_back(n, base_state, i);
    }

    // 3) Repeat step 2 until 2n/(k+1) bits remain
    for (int r = 1; r < k && X.size() > 0; r++) {
        LogPrint("pow", "Round %d:\n", r);
        // 2a) Sort the list
        LogPrint("pow", "- Sorting list\n");
        std::sort(X.begin(), X.end());

        LogPrint("pow", "- Finding collisions\n");
        int i = 0;
        int posFree = 0;
        std::vector<StepRow> Xc;
        while (i < X.size() - 1) {
            // 2b) Find next set of unordered pairs with collisions on the next n/(k+1) bits
            int j = 1;
            while (i+j < X.size() &&
                    HasCollision(X[i], X[i+j], CollisionByteLength())) {
                j++;
            }

            // 2c) Calculate tuples (X_i ^ X_j, (i, j))
            for (int l = 0; l < j - 1; l++) {
                for (int m = l + 1; m < j; m++) {
                    if (DistinctIndices(X[i+l], X[i+m])) {
                        Xc.push_back(X[i+l] ^ X[i+m]);
                        Xc.back().TrimHash(CollisionByteLength());
                    }
                }
            }

            // 2d) Store tuples on the table in-place if possible
            while (posFree < i+j && Xc.size() > 0) {
                X[posFree++] = Xc.back();
                Xc.pop_back();
            }

            i += j;
        }

        // 2e) Handle edge case where final table entry has no collision
        while (posFree < X.size() && Xc.size() > 0) {
            X[posFree++] = Xc.back();
            Xc.pop_back();
        }

        if (Xc.size() > 0) {
            // 2f) Add overflow to end of table
            X.insert(X.end(), Xc.begin(), Xc.end());
        } else if (posFree < X.size()) {
            // 2g) Remove empty space at the end
            X.erase(X.begin()+posFree, X.end());
            X.shrink_to_fit();
        }
    }

    // k+1) Find a collision on last 2n(k+1) bits
    LogPrint("pow", "Final round:\n");
    std::set<std::vector<eh_index>> solns;
    if (X.size() > 1) {
        LogPrint("pow", "- Sorting list\n");
        std::sort(X.begin(), X.end());
        LogPrint("pow", "- Finding collisions\n");
        for (int i = 0; i < X.size() - 1; i++) {
            StepRow res = X[i] ^ X[i+1];
            if (res.IsZero() && DistinctIndices(X[i], X[i+1])) {
                solns.insert(res.GetSolution());
            }
        }
    } else
        LogPrint("pow", "- List is empty\n");

    return solns;
}

bool Equihash::IsValidSolution(const eh_HashState& base_state, std::vector<eh_index> soln)
{
    eh_index soln_size { 1u << k };
    if (soln.size() != soln_size) {
        LogPrint("pow", "Invalid solution size: %d\n", soln.size());
        return false;
    }

    std::vector<StepRow> X;
    X.reserve(soln_size);
    for (eh_index i : soln) {
        X.emplace_back(n, base_state, i);
    }

    while (X.size() > 1) {
        std::vector<StepRow> Xc;
        for (int i = 0; i < X.size(); i += 2) {
            if (!HasCollision(X[i], X[i+1], CollisionByteLength())) {
                LogPrint("pow", "Invalid solution: invalid collision length between StepRows\n");
                LogPrint("pow", "X[i]   = %s\n", X[i].GetHex());
                LogPrint("pow", "X[i+1] = %s\n", X[i+1].GetHex());
                return false;
            }
            if (X[i+1].IndicesBefore(X[i])) {
                return false;
                LogPrint("pow", "Invalid solution: Index tree incorrectly ordered\n");
            }
            if (!DistinctIndices(X[i], X[i+1])) {
                LogPrint("pow", "Invalid solution: duplicate indices\n");
                return false;
            }
            Xc.push_back(X[i] ^ X[i+1]);
            Xc.back().TrimHash(CollisionByteLength());
        }
        X = Xc;
    }

    assert(X.size() == 1);
    return X[0].IsZero();
}
<|endoftext|>


<commit_before>#include <cstdlib>
#include <iostream>
#include <vector>
#include <sstream>
#include <string>

#include <hdf5.h>
#include <hdf5_hl.h>

#include "tppmatrix.h"
#include "omxmatrix.h"

#include "stdutil.h"
#include "define.h"

int convertMat2h5(char *);
int convertH5toMat(char *);
string get_h5_name(char *filename);
string get_tpp_name(char *filename);

int copy_data(TPPMatrix*, OMXMatrix*, int, int, vector<string>&);
int copy_data(OMXMatrix*, TPPMatrix*, int, int);

hid_t _memspace = -1;
hid_t _dataspace = -1;

int main(int argc, char* argv[])
{
    // Get cmdline parameters
    // for each input .mat file
    cout << "\nCube MAT/OMX Converter (built " << __DATE__ << ")\n";
    int errors = 0;

    for (int i=1; i<argc; i++) {
        char *tpfilename = argv[i];
        printf("\n\nConverting %s to ",tpfilename);

        // Figure out which way we're converting:
        string str(tpfilename);
        int v;

        if (((size_t)str.find(".omx")==string::npos) &&
            ((size_t)str.find(".OMX")==string::npos)) {
            v = convertMat2h5(tpfilename);
        } else {
            v = convertH5toMat(tpfilename);
        }

        if (v!=0) {
            printf(">> Failed to convert %s.",tpfilename);
            errors += v;
        }
    }

    printf("\nDone; %d errors and %d of %d completed.\n",errors,argc-errors-1,argc-1);
}

int convertMat2h5(char *filename) {
    int rows, cols, tables, rtn;
    TPPMatrix *matrix;
    OMXMatrix *omx;

    vector<string> matNames;

    try {
        // try to open file
        matrix = new TPPMatrix();
        matrix->openFile(filename);

        // get tp+ parameters such as zones, tables, names.
        rows = cols = matrix->getZones();

        tables = matrix->getTables();
        for (int t=1; t<=tables; t++) {
            string name(matrix->getTableName(t));
            matNames.push_back(name);
        }

        // Create OMX file
        string h5_name = get_h5_name(filename);
        omx = new OMXMatrix();
        omx->createFile(tables, rows, cols, matNames, h5_name);

        // Copy data
        rtn = copy_data(matrix, omx, rows, tables, matNames);

        // All done
        matrix->closeFile();
        omx->closeFile();

    } catch (TPPMatrix::FileOpenException&) {
        printf("Can't open %s.",filename);
        return 1;
    }

    return 0;
}

int convertH5toMat(char *filename) {
    int zones, tables, rtn;
    TPPMatrix *tpp;
    OMXMatrix *omx;
    const char* tnames[MAX_TABLES];

    try {
        // Open h5 file and get dimensions, table names
        omx = new OMXMatrix();
        omx->openFile(filename);

        tables = omx->getTables();
        zones  = omx->getRows();

        for (int t=1; t<=tables; t++) {
            tnames[t-1]=omx->getTableName(t).c_str();
        }

        // create TPP file
        string tppname = get_tpp_name(filename);
        tpp = new TPPMatrix();
        tpp->createFile(tables, zones, tnames, tppname.c_str());

        // Copy data
        rtn = copy_data(omx, tpp, zones, tables);

        /* Close the files. */
        tpp->closeFile();
        omx->closeFile();

    } catch (TPPMatrix::FileOpenException&) {
        printf("Can't open %s.",filename);
        return 1;
    }

    return 0;
}

// Copy from HDF5 to TPP:
int copy_data(OMXMatrix *omx, TPPMatrix *matrix, int zones, int tables) {

    // Set up some scratch space for reading row data
    double *rowdata = matrix->allocateRowBuffer();

    int row;
    printf("\n");

    string tableNames[MAX_TABLES+1];
    for (int t = 1; t <=tables; t++) {
        tableNames[t] = omx->getTableName(t);
    }

    // Loop on all rows
    for (row=1;row<=zones;row++) {
        if (row % 127 ==0) printf("Zone: %d\r",row);

        // Loop for each table
        for (int t=1;t<=tables;t++) {
            // Grab a row of data
            omx->getRow(tableNames[t], row, rowdata);

            // And write it to h5
            matrix->writeRow(t, row, rowdata);
        }
    }

    printf("Zone: %d\n",row-1);
    free(rowdata);
    return 0;
}

// Copy from TPP to HDF5:
int copy_data(TPPMatrix *matrix, OMXMatrix *omx, int zones, int tables, vector<string> &matNames) {
    double*     rowdata;

    // Set up some scratch space for reading row data
    rowdata = matrix->allocateRowBuffer();

    // Loop for each row
    int col;
    for (col=1;col<=zones;col++) {
        if (col %47 == 1) printf("\r%d tables:  zone %d     ",tables, col);
        for (int t=1; t<=tables; t++) {
            // Grab a row of data
            try {
                matrix->getRow(t, col, rowdata);
            } catch (TPPMatrix::MatrixReadException&) {
                    fprintf(stderr, "ERROR: Can't read table row %d in table %d!\n", col, t);
                    exit(2);
            }

            // And write it to h5
            omx->writeRow(matNames[t-1], col, rowdata);
        }
    }

    // Clean up
    printf("\r%d tables:  zone %d     \n",tables, col-1);

    free(rowdata);
    return 0;
}

// Replace .mat with .h5 in filename
string get_h5_name(char *filename) {

    string str(filename);
    size_t dot = str.rfind('.');

    str.erase(dot,4);
    str.insert(dot,".omx");
    printf("%s\n",str.c_str());

    return str;
}

// Replace .mat with .h5 in filename
string get_tpp_name(char *filename) {

    string str(filename);
    size_t dot = str.rfind('.');

    str.erase(dot,4);
    str.insert(dot,".mat");
    printf("%s\n",str.c_str());

    return str;
}

<commit_msg>Add usage msg<commit_after>#include <cstdlib>
#include <iostream>
#include <vector>
#include <sstream>
#include <string>

#include <hdf5.h>
#include <hdf5_hl.h>

#include "tppmatrix.h"
#include "omxmatrix.h"

#include "stdutil.h"
#include "define.h"

int convertMat2h5(char *);
int convertH5toMat(char *);
string get_h5_name(char *filename);
string get_tpp_name(char *filename);

int copy_data(TPPMatrix*, OMXMatrix*, int, int, vector<string>&);
int copy_data(OMXMatrix*, TPPMatrix*, int, int);

hid_t _memspace = -1;
hid_t _dataspace = -1;

int main(int argc, char* argv[])
{
    // Get cmdline parameters
    // for each input .mat file
    cout << "\nCube MAT/OMX Converter (built " << __DATE__ << ")\n";
    int errors = 0;

    if (argc==1) {
		cout << "\nUsage:  cube2omx.exe  [filename1] [filename2] ...\n";
		cout << "        - Files ending in .omx or .OMX will be converted from OMX to Cube\n";
		cout << "        - Files ending in anything else will be converted from Cube to OMX\n\n";
		exit(0);
    }

    for (int i=1; i<argc; i++) {
        char *tpfilename = argv[i];
        printf("\n\nConverting %s to ",tpfilename);

        // Figure out which way we're converting:
        string str(tpfilename);
        int v;

        if (((size_t)str.find(".omx")==string::npos) &&
            ((size_t)str.find(".OMX")==string::npos)) {
            v = convertMat2h5(tpfilename);
        } else {
            v = convertH5toMat(tpfilename);
        }

        if (v!=0) {
            printf(">> Failed to convert %s.",tpfilename);
            errors += v;
        }
    }

    printf("\nDone; %d errors and %d of %d completed.\n",errors,argc-errors-1,argc-1);
}

int convertMat2h5(char *filename) {
    int rows, cols, tables, rtn;
    TPPMatrix *matrix;
    OMXMatrix *omx;

    vector<string> matNames;

    try {
        // try to open file
        matrix = new TPPMatrix();
        matrix->openFile(filename);

        // get tp+ parameters such as zones, tables, names.
        rows = cols = matrix->getZones();

        tables = matrix->getTables();
        for (int t=1; t<=tables; t++) {
            string name(matrix->getTableName(t));
            matNames.push_back(name);
        }

        // Create OMX file
        string h5_name = get_h5_name(filename);
        omx = new OMXMatrix();
        omx->createFile(tables, rows, cols, matNames, h5_name);

        // Copy data
        rtn = copy_data(matrix, omx, rows, tables, matNames);

        // All done
        matrix->closeFile();
        omx->closeFile();

    } catch (TPPMatrix::FileOpenException&) {
        printf("Can't open %s.",filename);
        return 1;
    }

    return 0;
}

int convertH5toMat(char *filename) {
    int zones, tables, rtn;
    TPPMatrix *tpp;
    OMXMatrix *omx;
    const char* tnames[MAX_TABLES];

    try {
        // Open h5 file and get dimensions, table names
        omx = new OMXMatrix();
        omx->openFile(filename);

        tables = omx->getTables();
        zones  = omx->getRows();

        for (int t=1; t<=tables; t++) {
            tnames[t-1]=omx->getTableName(t).c_str();
        }

        // create TPP file
        string tppname = get_tpp_name(filename);
        tpp = new TPPMatrix();
        tpp->createFile(tables, zones, tnames, tppname.c_str());

        // Copy data
        rtn = copy_data(omx, tpp, zones, tables);

        /* Close the files. */
        tpp->closeFile();
        omx->closeFile();

    } catch (TPPMatrix::FileOpenException&) {
        printf("Can't open %s.",filename);
        return 1;
    }

    return 0;
}

// Copy from HDF5 to TPP:
int copy_data(OMXMatrix *omx, TPPMatrix *matrix, int zones, int tables) {

    // Set up some scratch space for reading row data
    double *rowdata = matrix->allocateRowBuffer();

    int row;
    printf("\n");

    string tableNames[MAX_TABLES+1];
    for (int t = 1; t <=tables; t++) {
        tableNames[t] = omx->getTableName(t);
    }

    // Loop on all rows
    for (row=1;row<=zones;row++) {
        if (row % 127 ==0) printf("Zone: %d\r",row);

        // Loop for each table
        for (int t=1;t<=tables;t++) {
            // Grab a row of data
            omx->getRow(tableNames[t], row, rowdata);

            // And write it to h5
            matrix->writeRow(t, row, rowdata);
        }
    }

    printf("Zone: %d\n",row-1);
    free(rowdata);
    return 0;
}

// Copy from TPP to HDF5:
int copy_data(TPPMatrix *matrix, OMXMatrix *omx, int zones, int tables, vector<string> &matNames) {
    double*     rowdata;

    // Set up some scratch space for reading row data
    rowdata = matrix->allocateRowBuffer();

    // Loop for each row
    int col;
    for (col=1;col<=zones;col++) {
        if (col %47 == 1) printf("\r%d tables:  zone %d     ",tables, col);
        for (int t=1; t<=tables; t++) {
            // Grab a row of data
            try {
                matrix->getRow(t, col, rowdata);
            } catch (TPPMatrix::MatrixReadException&) {
                    fprintf(stderr, "ERROR: Can't read table row %d in table %d!\n", col, t);
                    exit(2);
            }

            // And write it to h5
            omx->writeRow(matNames[t-1], col, rowdata);
        }
    }

    // Clean up
    printf("\r%d tables:  zone %d     \n",tables, col-1);

    free(rowdata);
    return 0;
}

// Replace .mat with .h5 in filename
string get_h5_name(char *filename) {

    string str(filename);
    size_t dot = str.rfind('.');

    str.erase(dot,4);
    str.insert(dot,".omx");
    printf("%s\n",str.c_str());

    return str;
}

// Replace .mat with .h5 in filename
string get_tpp_name(char *filename) {

    string str(filename);
    size_t dot = str.rfind('.');

    str.erase(dot,4);
    str.insert(dot,".mat");
    printf("%s\n",str.c_str());

    return str;
}

<|endoftext|>


<commit_before>// -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
// Small example how write text.
//
// This code is public domain
// (but note, that the led-matrix library this depends on is GPL v2)

#include "led-matrix.h"
#include "graphics.h"

#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

using namespace rgb_matrix;

static int usage(const char *progname) {
  fprintf(stderr, "usage: %s [options]\n", progname);
  fprintf(stderr, "Reads text from stdin and displays it. "
          "Empty string: clear screen\n");
  fprintf(stderr, "Options:\n"
          "\t-f <font-file>: Use given font.\n"
          "\t-r <rows>     : Display rows. 16 for 16x32, 32 for 32x32. "
          "Default: 32\n"
          "\t-c <chained>  : Daisy-chained boards. Default: 1.\n"
          "\t-x <x-origin> : X-Origin of displaying text (Default: 0)\n"
          "\t-y <y-origin> : Y-Origin of displaying text (Default: 0)\n"
          "\t-C <r,g,b>    : Color. Default 255,255,0\n");
  return 1;
}

static bool parseColor(Color *c, const char *str) {
  return sscanf(str, "%hhu,%hhu,%hhu", &c->r, &c->g, &c->b) == 3;
}

int main(int argc, char *argv[]) {
  const char *line = NULL;
  Color color(255, 0, 0);
  const char *bdf_font_file = "/root/rpi-rgb-led-matrix/fonts/7x14.bdf";
  int rows = 16;
  int chain = 3;
  int x_orig = 1;
  int y_orig = 1;
  int microseconds = 2000000;

  int opt;
  while ((opt = getopt(argc, argv, "r:c:x:y:f:t:m:C:")) != -1) {
    switch (opt) {
    case 'r': rows = atoi(optarg); break;
    case 'c': chain = atoi(optarg); break;
    case 'x': x_orig = atoi(optarg); break;
    case 'y': y_orig = atoi(optarg); break;
    case 'm': microseconds = atoi(optarg); break;
    case 'f': bdf_font_file = strdup(optarg); break;
    case 't': line = strdup(optarg); break;
    case 'C':
      if (!parseColor(&color, optarg)) {
        fprintf(stderr, "Invalid color spec.\n");
        return usage(argv[0]);
      }
      break;
    }
  }

  if (bdf_font_file == NULL) {
    fprintf(stderr, "Need to specify BDF font-file with -f\n");
    return usage(argv[0]);
  }

  /*
   * Load font. This needs to be a filename with a bdf bitmap font.
   */
  rgb_matrix::Font font;
  if (!font.LoadFont(bdf_font_file)) {
    fprintf(stderr, "Couldn't load font '%s'\n", bdf_font_file);
    return usage(argv[0]);
  }

  /*
   * Set up GPIO pins. This fails when not running as root.
   */
  GPIO io;
  if (!io.Init())
    return 1;

  /*
   * Set up the RGBMatrix. It implements a 'Canvas' interface.
   */
  RGBMatrix *canvas = new RGBMatrix(&io, rows, chain);

  bool all_extreme_colors = true;
  all_extreme_colors &= color.r == 0 || color.r == 255;
  all_extreme_colors &= color.g == 0 || color.g == 255;
  all_extreme_colors &= color.b == 0 || color.b == 255;
  if (all_extreme_colors)
    canvas->SetPWMBits(1);

  const int x = x_orig;
  int y = y_orig;

  bool line_empty = strlen(line) == 0;
  if ((y + font.height() > canvas->height()) || line_empty) {
    canvas->Clear();
    y = y_orig;
  }
  if (line_empty)
    return -1;
  rgb_matrix::DrawText(canvas, font, x, y + font.baseline(), color, line);
  y += font.height();
  usleep(microseconds);
  // Finished. Shut down the RGB matrix.
  canvas->Clear();
  delete canvas;

  return 0;
}
<commit_msg>Relative font path, micro to seconds<commit_after>// -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; -*-
// Small example how write text.
//
// This code is public domain
// (but note, that the led-matrix library this depends on is GPL v2)

#include "led-matrix.h"
#include "graphics.h"

#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

using namespace rgb_matrix;

static int usage(const char *progname) {
  fprintf(stderr, "usage: %s [options]\n", progname);
  fprintf(stderr, "Reads text from stdin and displays it. "
          "Empty string: clear screen\n");
  fprintf(stderr, "Options:\n"
          "\t-t <text>: Text to be displayed (<= 13 chars).\n"
          "\t-s <seconds>: seconds to sleep.\n"
          "\t-f <font-file>: Use given font.\n"
          "\t-r <rows>     : Display rows. 16 for 16x32, 32 for 32x32. "
          "Default: 32\n"
          "\t-c <chained>  : Daisy-chained boards. Default: 1.\n"
          "\t-x <x-origin> : X-Origin of displaying text (Default: 0)\n"
          "\t-y <y-origin> : Y-Origin of displaying text (Default: 0)\n"
          "\t-C <r,g,b>    : Color. Default 255,255,0\n");
  return 1;
}

static bool parseColor(Color *c, const char *str) {
  return sscanf(str, "%hhu,%hhu,%hhu", &c->r, &c->g, &c->b) == 3;
}

int main(int argc, char *argv[]) {
  Color color(0, 105, 166);
  const char *line = NULL;
  const char *bdf_font_file = "./fonts/7x14.bdf";
  unsigned int rows = 16;
  unsigned int chain = 3;
  unsigned int x_orig = 1;
  unsigned int y_orig = 1;
  unsigned int seconds = 2;

  int opt;
  while ((opt = getopt(argc, argv, "r:c:x:y:s:f:t:C:")) != -1) {
    switch (opt) {
    case 'r': rows = atoi(optarg); break;
    case 'c': chain = atoi(optarg); break;
    case 'x': x_orig = atoi(optarg); break;
    case 'y': y_orig = atoi(optarg); break;
    case 's': seconds = atoi(optarg); break;
    case 'f': bdf_font_file = strdup(optarg); break;
    case 't': line = strdup(optarg); break;
    case 'C':
      if (!parseColor(&color, optarg)) {
        fprintf(stderr, "Invalid color spec.\n");
        return usage(argv[0]);
      }
      break;
    }
  }

  if (bdf_font_file == NULL) {
    fprintf(stderr, "Need to specify BDF font-file with -f\n");
    return usage(argv[0]);
  }

  /*
   * Load font. This needs to be a filename with a bdf bitmap font.
   */
  rgb_matrix::Font font;
  if (!font.LoadFont(bdf_font_file)) {
    fprintf(stderr, "Couldn't load font '%s'\n", bdf_font_file);
    return usage(argv[0]);
  }

  /*
   * Set up GPIO pins. This fails when not running as root.
   */
  GPIO io;
  if (!io.Init())
    return 1;

  /*
   * Set up the RGBMatrix. It implements a 'Canvas' interface.
   */
  RGBMatrix *canvas = new RGBMatrix(&io, rows, chain);

  bool all_extreme_colors = true;
  all_extreme_colors &= color.r == 0 || color.r == 255;
  all_extreme_colors &= color.g == 0 || color.g == 255;
  all_extreme_colors &= color.b == 0 || color.b == 255;
  if (all_extreme_colors)
    canvas->SetPWMBits(1);

  const int x = x_orig;
  int y = y_orig;

  bool line_empty = strlen(line) == 0;
  if ((y + font.height() > canvas->height()) || line_empty) {
    canvas->Clear();
    y = y_orig;
  }
  if (line_empty)
    return -1;
  rgb_matrix::DrawText(canvas, font, x, y + font.baseline(), color, line);
  y += font.height();
  usleep(seconds*1000000);
  // Finished. Shut down the RGB matrix.
  canvas->Clear();
  delete canvas;

  return 0;
}
<|endoftext|>


<commit_before>#ifndef ITER_CYCLE_H_
#define ITER_CYCLE_H_

#include "iterbase.hpp"

#include <utility>
#include <iterator>
#include <initializer_list>

namespace iter {

    //Forward declarations of Cycle and cycle
    template <typename Container>
    class Cycle;

    template <typename Container>
    Cycle<Container> cycle(Container&&);

    template <typename T>
    Cycle<std::initializer_list<T>> cycle(std::initializer_list<T>);

    template <typename Container>
    class Cycle {
        private:
            // The cycle function is the only thing allowed to create a Cycle
            friend Cycle cycle<Container>(Container&&);
            template <typename T>
            friend Cycle<std::initializer_list<T>> cycle(
                    std::initializer_list<T>);

            Container container;

            // Value constructor for use only in the cycle function
            Cycle(Container container)
                : container(std::forward<Container>(container))
            { }

        public:
            class Iterator
                : public std::iterator<std::input_iterator_tag,
                            iterator_traits_deref<Container>>
            {
                private:
                    using iter_type = iterator_type<Container>;
                    iterator_type<Container> sub_iter;
                    const iterator_type<Container> begin;
                    const iterator_type<Container> end;
                public:
                    Iterator (iterator_type<Container> iter,
                            iterator_type<Container> end)
                        : sub_iter{iter},
                        begin{iter},
                        end{end}
                    { }

                    iterator_deref<Container> operator*() const {
                        return *this->sub_iter;
                    }

                    Iterator& operator++() {
                        ++this->sub_iter;
                        // reset to beginning upon reaching the end
                        if (!(this->sub_iter != this->end)) {
                            // explicit destruction with placement new in order
                            // to support iterators with no operator=
                            this->sub_iter.~iter_type();
                            new(&this->sub_iter) iterator_type<Container>(
                                    this->begin);
                        }
                        return *this;
                    }

                    constexpr bool operator!=(const Iterator&) const {
                        return true;
                    }
            };

            Iterator begin() {
                return {std::begin(this->container),
                        std::end(this->container)};
            }

            Iterator end() {
                return {std::end(this->container),
                        std::end(this->container)};
            }

    };

    // Helper function to instantiate a Cycle
    template <typename Container>
    Cycle<Container> cycle(Container&& container) {
        return {std::forward<Container>(container)};
    }

    template <typename T>
    Cycle<std::initializer_list<T>> cycle(std::initializer_list<T> il)
    {
        return {std::move(il)};
    }
}

#endif
<commit_msg>adds cycle iter ==<commit_after>#ifndef ITER_CYCLE_H_
#define ITER_CYCLE_H_

#include "iterbase.hpp"

#include <utility>
#include <iterator>
#include <initializer_list>

namespace iter {

    //Forward declarations of Cycle and cycle
    template <typename Container>
    class Cycle;

    template <typename Container>
    Cycle<Container> cycle(Container&&);

    template <typename T>
    Cycle<std::initializer_list<T>> cycle(std::initializer_list<T>);

    template <typename Container>
    class Cycle {
        private:
            // The cycle function is the only thing allowed to create a Cycle
            friend Cycle cycle<Container>(Container&&);
            template <typename T>
            friend Cycle<std::initializer_list<T>> cycle(
                    std::initializer_list<T>);

            Container container;

            // Value constructor for use only in the cycle function
            Cycle(Container container)
                : container(std::forward<Container>(container))
            { }

        public:
            class Iterator
                : public std::iterator<std::input_iterator_tag,
                            iterator_traits_deref<Container>>
            {
                private:
                    using iter_type = iterator_type<Container>;
                    iterator_type<Container> sub_iter;
                    const iterator_type<Container> begin;
                    const iterator_type<Container> end;
                public:
                    Iterator (iterator_type<Container> iter,
                            iterator_type<Container> end)
                        : sub_iter{iter},
                        begin{iter},
                        end{end}
                    { }

                    iterator_deref<Container> operator*() const {
                        return *this->sub_iter;
                    }

                    Iterator& operator++() {
                        ++this->sub_iter;
                        // reset to beginning upon reaching the end
                        if (!(this->sub_iter != this->end)) {
                            // explicit destruction with placement new in order
                            // to support iterators with no operator=
                            this->sub_iter.~iter_type();
                            new(&this->sub_iter) iterator_type<Container>(
                                    this->begin);
                        }
                        return *this;
                    }

                    constexpr bool operator!=(const Iterator&) const {
                        return true;
                    }

                    bool operator==(const Iterator& other) const {
                        return !(*this != other);
                    }
            };

            Iterator begin() {
                return {std::begin(this->container),
                        std::end(this->container)};
            }

            Iterator end() {
                return {std::end(this->container),
                        std::end(this->container)};
            }

    };

    // Helper function to instantiate a Cycle
    template <typename Container>
    Cycle<Container> cycle(Container&& container) {
        return {std::forward<Container>(container)};
    }

    template <typename T>
    Cycle<std::initializer_list<T>> cycle(std::initializer_list<T> il)
    {
        return {std::move(il)};
    }
}

#endif
<|endoftext|>


<commit_before>#pragma once

/**
 * Central place to store Solver config options
 */

#include <cstdio>
#include <vector>
#include <cmath>

struct SolverOptions
{
  double residual;
  int max_iters, restart;
  unsigned max_p, p_min;
  bool variable_p;

  enum relaxation_type { SIMONCINI, BOURAS };

  relaxation_type relax_type;

  SolverOptions(double r, int m_iters, unsigned p) : residual(r), max_iters(m_iters), restart(50), max_p(p), variable_p(false), relax_type(BOURAS) {};
  SolverOptions() : residual(1e-5), max_iters(500), restart(500), max_p(16), p_min(5), variable_p(true), relax_type(BOURAS) {};

  unsigned predict_p(double eps) const {
    // if no relaxation, return default p
    if (!this->variable_p) return max_p;
    // relaxation from Bouras & Fraysse
    if (this->relax_type == BOURAS) {
      double alpha = 1. / std::min(eps, 1.);
      double nu = std::min(alpha*this->residual,1.);
      // predict p for Spherical Laplace kernel -- abstract out
      return std::min((unsigned)ceil(-log(nu)),max_p);
    } else if (this->relax_type == SIMONCINI) {
      return std::min((unsigned)ceil(-log(eps)),max_p);
    }
    return max_p;
  }
};

<commit_msg>change log to log2 in predict_p function<commit_after>#pragma once

/**
 * Central place to store Solver config options
 */

#include <cstdio>
#include <vector>
#include <cmath>

struct SolverOptions
{
  double residual;
  int max_iters, restart;
  unsigned max_p, p_min;
  bool variable_p;

  enum relaxation_type { SIMONCINI, BOURAS };

  relaxation_type relax_type;

  SolverOptions(double r, int m_iters, unsigned p) : residual(r), max_iters(m_iters), restart(50), max_p(p), variable_p(false), relax_type(BOURAS) {};
  SolverOptions() : residual(1e-5), max_iters(500), restart(500), max_p(16), p_min(5), variable_p(true), relax_type(BOURAS) {};

  unsigned predict_p(double eps) const {
    // if no relaxation, return default p
    if (!this->variable_p) return max_p;
    // relaxation from Bouras & Fraysse
    if (this->relax_type == BOURAS) {
      double alpha = 1. / std::min(eps, 1.);
      double nu = std::min(alpha*this->residual,1.);
      // predict p for Spherical Laplace kernel -- abstract out
      return std::min((unsigned)ceil(-log2(nu)),max_p);
    } else if (this->relax_type == SIMONCINI) {
      return std::min((unsigned)ceil(-log2(eps)),max_p);
    }
    return max_p;
  }
};

<|endoftext|>


<commit_before>// Copyright (c) 2012-2017 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "util.h"
#include "utiltime.h"
#include "validation.h"

#include "test/test_bitcoin.h"
#include "checkqueue.h"
#include <boost/test/unit_test.hpp>
#include <boost/thread.hpp>
#include <atomic>
#include <thread>
#include <vector>
#include <mutex>
#include <condition_variable>

#include <unordered_set>
#include <memory>
#include "random.h"

// BasicTestingSetup not sufficient because nScriptCheckThreads is not set
// otherwise.
BOOST_FIXTURE_TEST_SUITE(checkqueue_tests, TestingSetup)

static const int QUEUE_BATCH_SIZE = 128;

struct FakeCheck {
    bool operator()()
    {
        return true;
    }
    void swap(FakeCheck& x){};
};

struct FakeCheckCheckCompletion {
    static std::atomic<size_t> n_calls;
    bool operator()()
    {
        ++n_calls;
        return true;
    }
    void swap(FakeCheckCheckCompletion& x){};
};

struct FailingCheck {
    bool fails;
    FailingCheck(bool _fails) : fails(_fails){};
    FailingCheck() : fails(true){};
    bool operator()()
    {
        return !fails;
    }
    void swap(FailingCheck& x)
    {
        std::swap(fails, x.fails);
    };
};

struct UniqueCheck {
    static std::mutex m;
    static std::unordered_multiset<size_t> results;
    size_t check_id;
    UniqueCheck(size_t check_id_in) : check_id(check_id_in){};
    UniqueCheck() : check_id(0){};
    bool operator()()
    {
        std::lock_guard<std::mutex> l(m);
        results.insert(check_id);
        return true;
    }
    void swap(UniqueCheck& x) { std::swap(x.check_id, check_id); };
};

struct MemoryCheck {
    static std::atomic<size_t> fake_allocated_memory;
    bool b {false};
    bool operator()()
    {
        return true;
    }
    MemoryCheck(){};
    MemoryCheck(const MemoryCheck& x)
    {
        // We have to do this to make sure that destructor calls are paired
        //
        // Really, copy constructor should be deletable, but CCheckQueue breaks
        // if it is deleted because of internal push_back.
        fake_allocated_memory += b;
    };
    MemoryCheck(bool b_) : b(b_)
    {
        fake_allocated_memory += b;
    };
    ~MemoryCheck(){
        fake_allocated_memory -= b;

    };
    void swap(MemoryCheck& x) { std::swap(b, x.b); };
};

struct FrozenCleanupCheck {
    static std::atomic<uint64_t> nFrozen;
    static std::condition_variable cv;
    static std::mutex m;
    // Freezing can't be the default initialized behavior given how the queue
    // swaps in default initialized Checks.
    bool should_freeze {false};
    bool operator()()
    {
        return true;
    }
    FrozenCleanupCheck() {}
    ~FrozenCleanupCheck()
    {
        if (should_freeze) {
            std::unique_lock<std::mutex> l(m);
            nFrozen = 1;
            cv.notify_one();
            cv.wait(l, []{ return nFrozen == 0;});
        }
    }
    void swap(FrozenCleanupCheck& x){std::swap(should_freeze, x.should_freeze);};
};

// Static Allocations
std::mutex FrozenCleanupCheck::m{};
std::atomic<uint64_t> FrozenCleanupCheck::nFrozen{0};
std::condition_variable FrozenCleanupCheck::cv{};
std::mutex UniqueCheck::m;
std::unordered_multiset<size_t> UniqueCheck::results;
std::atomic<size_t> FakeCheckCheckCompletion::n_calls{0};
std::atomic<size_t> MemoryCheck::fake_allocated_memory{0};

// Queue Typedefs
typedef CCheckQueue<FakeCheckCheckCompletion> Correct_Queue;
typedef CCheckQueue<FakeCheck> Standard_Queue;
typedef CCheckQueue<FailingCheck> Failing_Queue;
typedef CCheckQueue<UniqueCheck> Unique_Queue;
typedef CCheckQueue<MemoryCheck> Memory_Queue;
typedef CCheckQueue<FrozenCleanupCheck> FrozenCleanup_Queue;

/** This test case checks that the CCheckQueue works properly
 * with each specified size_t Checks pushed.
 */
void Correct_Queue_range(std::vector<size_t> range)
{
    auto small_queue = std::unique_ptr<Correct_Queue>(new Correct_Queue {QUEUE_BATCH_SIZE});
    boost::thread_group tg;
    for (auto x = 0; x < nScriptCheckThreads; ++x) {
       tg.create_thread([&]{small_queue->Thread();});
    }
    // Make vChecks here to save on malloc (this test can be slow...)
    std::vector<FakeCheckCheckCompletion> vChecks;
    for (auto i : range) {
        size_t total = i;
        FakeCheckCheckCompletion::n_calls = 0;
        CCheckQueueControl<FakeCheckCheckCompletion> control(small_queue.get());
        while (total) {
            vChecks.resize(std::min(total, (size_t) InsecureRandRange(10)));
            total -= vChecks.size();
            control.Add(vChecks);
        }
        BOOST_REQUIRE(control.Wait());
        if (FakeCheckCheckCompletion::n_calls != i) {
            BOOST_REQUIRE_EQUAL(FakeCheckCheckCompletion::n_calls, i);
            BOOST_TEST_MESSAGE("Failure on trial " << i << " expected, got " << FakeCheckCheckCompletion::n_calls);
        }
    }
    tg.interrupt_all();
    tg.join_all();
}

/** Test that 0 checks is correct
 */
BOOST_AUTO_TEST_CASE(test_CheckQueue_Correct_Zero)
{
    std::vector<size_t> range;
    range.push_back((size_t)0);
    Correct_Queue_range(range);
}
/** Test that 1 check is correct
 */
BOOST_AUTO_TEST_CASE(test_CheckQueue_Correct_One)
{
    std::vector<size_t> range;
    range.push_back((size_t)1);
    Correct_Queue_range(range);
}
/** Test that MAX check is correct
 */
BOOST_AUTO_TEST_CASE(test_CheckQueue_Correct_Max)
{
    std::vector<size_t> range;
    range.push_back(100000);
    Correct_Queue_range(range);
}
/** Test that random numbers of checks are correct
 */
BOOST_AUTO_TEST_CASE(test_CheckQueue_Correct_Random)
{
    std::vector<size_t> range;
    range.reserve(100000/1000);
    for (size_t i = 2; i < 100000; i += std::max((size_t)1, (size_t)InsecureRandRange(std::min((size_t)1000, ((size_t)100000) - i))))
        range.push_back(i);
    Correct_Queue_range(range);
}

/** Test that failing checks are caught */
BOOST_AUTO_TEST_CASE(test_CheckQueue_Catches_Failure)
{
    auto fail_queue = std::unique_ptr<Failing_Queue>(new Failing_Queue {QUEUE_BATCH_SIZE});

    boost::thread_group tg;
    for (auto x = 0; x < nScriptCheckThreads; ++x) {
       tg.create_thread([&]{fail_queue->Thread();});
    }

    for (size_t i = 0; i < 1001; ++i) {
        CCheckQueueControl<FailingCheck> control(fail_queue.get());
        size_t remaining = i;
        while (remaining) {
            size_t r = InsecureRandRange(10);

            std::vector<FailingCheck> vChecks;
            vChecks.reserve(r);
            for (size_t k = 0; k < r && remaining; k++, remaining--)
                vChecks.emplace_back(remaining == 1);
            control.Add(vChecks);
        }
        bool success = control.Wait();
        if (i > 0) {
            BOOST_REQUIRE(!success);
        } else if (i == 0) {
            BOOST_REQUIRE(success);
        }
    }
    tg.interrupt_all();
    tg.join_all();
}
// Test that a block validation which fails does not interfere with
// future blocks, ie, the bad state is cleared.
BOOST_AUTO_TEST_CASE(test_CheckQueue_Recovers_From_Failure)
{
    auto fail_queue = std::unique_ptr<Failing_Queue>(new Failing_Queue {QUEUE_BATCH_SIZE});
    boost::thread_group tg;
    for (auto x = 0; x < nScriptCheckThreads; ++x) {
       tg.create_thread([&]{fail_queue->Thread();});
    }

    for (auto times = 0; times < 10; ++times) {
        for (bool end_fails : {true, false}) {
            CCheckQueueControl<FailingCheck> control(fail_queue.get());
            {
                std::vector<FailingCheck> vChecks;
                vChecks.resize(100, false);
                vChecks[99] = end_fails;
                control.Add(vChecks);
            }
            bool r =control.Wait();
            BOOST_REQUIRE(r || end_fails);
        }
    }
    tg.interrupt_all();
    tg.join_all();
}

// Test that unique checks are actually all called individually, rather than
// just one check being called repeatedly. Test that checks are not called
// more than once as well
BOOST_AUTO_TEST_CASE(test_CheckQueue_UniqueCheck)
{
    auto queue = std::unique_ptr<Unique_Queue>(new Unique_Queue {QUEUE_BATCH_SIZE});
    boost::thread_group tg;
    for (auto x = 0; x < nScriptCheckThreads; ++x) {
       tg.create_thread([&]{queue->Thread();});

    }

    size_t COUNT = 100000;
    size_t total = COUNT;
    {
        CCheckQueueControl<UniqueCheck> control(queue.get());
        while (total) {
            size_t r = InsecureRandRange(10);
            std::vector<UniqueCheck> vChecks;
            for (size_t k = 0; k < r && total; k++)
                vChecks.emplace_back(--total);
            control.Add(vChecks);
        }
    }
    bool r = true;
    BOOST_REQUIRE_EQUAL(UniqueCheck::results.size(), COUNT);
    for (size_t i = 0; i < COUNT; ++i)
        r = r && UniqueCheck::results.count(i) == 1;
    BOOST_REQUIRE(r);
    tg.interrupt_all();
    tg.join_all();
}

// Test that blocks which might allocate lots of memory free their memory aggressively.
//
// This test attempts to catch a pathological case where by lazily freeing
// checks might mean leaving a check un-swapped out, and decreasing by 1 each
// time could leave the data hanging across a sequence of blocks.
BOOST_AUTO_TEST_CASE(test_CheckQueue_Memory)
{
    auto queue = std::unique_ptr<Memory_Queue>(new Memory_Queue {QUEUE_BATCH_SIZE});
    boost::thread_group tg;
    for (auto x = 0; x < nScriptCheckThreads; ++x) {
       tg.create_thread([&]{queue->Thread();});
    }
    for (size_t i = 0; i < 1000; ++i) {
        size_t total = i;
        {
            CCheckQueueControl<MemoryCheck> control(queue.get());
            while (total) {
                size_t r = InsecureRandRange(10);
                std::vector<MemoryCheck> vChecks;
                for (size_t k = 0; k < r && total; k++) {
                    total--;
                    // Each iteration leaves data at the front, back, and middle
                    // to catch any sort of deallocation failure
                    vChecks.emplace_back(total == 0 || total == i || total == i/2);
                }
                control.Add(vChecks);
            }
        }
        BOOST_REQUIRE_EQUAL(MemoryCheck::fake_allocated_memory, 0);
    }
    tg.interrupt_all();
    tg.join_all();
}

// Test that a new verification cannot occur until all checks
// have been destructed
BOOST_AUTO_TEST_CASE(test_CheckQueue_FrozenCleanup)
{
    auto queue = std::unique_ptr<FrozenCleanup_Queue>(new FrozenCleanup_Queue {QUEUE_BATCH_SIZE});
    boost::thread_group tg;
    bool fails = false;
    for (auto x = 0; x < nScriptCheckThreads; ++x) {
        tg.create_thread([&]{queue->Thread();});
    }
    std::thread t0([&]() {
        CCheckQueueControl<FrozenCleanupCheck> control(queue.get());
        std::vector<FrozenCleanupCheck> vChecks(1);
        // Freezing can't be the default initialized behavior given how the queue
        // swaps in default initialized Checks (otherwise freezing destructor
        // would get called twice).
        vChecks[0].should_freeze = true;
        control.Add(vChecks);
        control.Wait(); // Hangs here
    });
    {
        std::unique_lock<std::mutex> l(FrozenCleanupCheck::m);
        // Wait until the queue has finished all jobs and frozen
        FrozenCleanupCheck::cv.wait(l, [](){return FrozenCleanupCheck::nFrozen == 1;});
        // Try to get control of the queue a bunch of times
        for (auto x = 0; x < 100 && !fails; ++x) {
            fails = queue->ControlMutex.try_lock();
        }
        // Unfreeze
        FrozenCleanupCheck::nFrozen = 0;
    }
    // Awaken frozen destructor
    FrozenCleanupCheck::cv.notify_one();
    // Wait for control to finish
    t0.join();
    tg.interrupt_all();
    tg.join_all();
    BOOST_REQUIRE(!fails);
}

/** Test that CCheckQueueControl is threadsafe */
BOOST_AUTO_TEST_CASE(test_CheckQueueControl_Locks)
{
    auto queue = std::unique_ptr<Standard_Queue>(new Standard_Queue{QUEUE_BATCH_SIZE});
    {
        boost::thread_group tg;
        std::atomic<int> nThreads {0};
        std::atomic<int> fails {0};
        for (size_t i = 0; i < 3; ++i) {
            tg.create_thread(
                    [&]{
                    CCheckQueueControl<FakeCheck> control(queue.get());
                    // While sleeping, no other thread should execute to this point
                    auto observed = ++nThreads;
                    MilliSleep(10);
                    fails += observed  != nThreads;
                    });
        }
        tg.join_all();
        BOOST_REQUIRE_EQUAL(fails, 0);
    }
    {
        boost::thread_group tg;
        std::mutex m;
        std::condition_variable cv;
        {
            bool has_lock {false};
            bool has_tried {false};
            bool done {false};
            bool done_ack {false};
            std::unique_lock<std::mutex> l(m);
            tg.create_thread([&]{
                    CCheckQueueControl<FakeCheck> control(queue.get());
                    std::unique_lock<std::mutex> ll(m);
                    has_lock = true;
                    cv.notify_one();
                    cv.wait(ll, [&]{return has_tried;});
                    done = true;
                    cv.notify_one();
                    // Wait until the done is acknowledged
                    //
                    cv.wait(ll, [&]{return done_ack;});
                    });
            // Wait for thread to get the lock
            cv.wait(l, [&](){return has_lock;});
            bool fails = false;
            for (auto x = 0; x < 100 && !fails; ++x) {
                fails = queue->ControlMutex.try_lock();
            }
            has_tried = true;
            cv.notify_one();
            cv.wait(l, [&](){return done;});
            // Acknowledge the done
            done_ack = true;
            cv.notify_one();
            BOOST_REQUIRE(!fails);
        }
        tg.join_all();
    }
}
BOOST_AUTO_TEST_SUITE_END()

<commit_msg>Expose more parallelism with relaxed atomics (suggested in #9938). Fix a test to check the exclusive or of two properties rather than just or.<commit_after>// Copyright (c) 2012-2017 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include "util.h"
#include "utiltime.h"
#include "validation.h"

#include "test/test_bitcoin.h"
#include "checkqueue.h"
#include <boost/test/unit_test.hpp>
#include <boost/thread.hpp>
#include <atomic>
#include <thread>
#include <vector>
#include <mutex>
#include <condition_variable>

#include <unordered_set>
#include <memory>
#include "random.h"

// BasicTestingSetup not sufficient because nScriptCheckThreads is not set
// otherwise.
BOOST_FIXTURE_TEST_SUITE(checkqueue_tests, TestingSetup)

static const int QUEUE_BATCH_SIZE = 128;

struct FakeCheck {
    bool operator()()
    {
        return true;
    }
    void swap(FakeCheck& x){};
};

struct FakeCheckCheckCompletion {
    static std::atomic<size_t> n_calls;
    bool operator()()
    {
        n_calls.fetch_add(1, std::memory_order_relaxed);
        return true;
    }
    void swap(FakeCheckCheckCompletion& x){};
};

struct FailingCheck {
    bool fails;
    FailingCheck(bool _fails) : fails(_fails){};
    FailingCheck() : fails(true){};
    bool operator()()
    {
        return !fails;
    }
    void swap(FailingCheck& x)
    {
        std::swap(fails, x.fails);
    };
};

struct UniqueCheck {
    static std::mutex m;
    static std::unordered_multiset<size_t> results;
    size_t check_id;
    UniqueCheck(size_t check_id_in) : check_id(check_id_in){};
    UniqueCheck() : check_id(0){};
    bool operator()()
    {
        std::lock_guard<std::mutex> l(m);
        results.insert(check_id);
        return true;
    }
    void swap(UniqueCheck& x) { std::swap(x.check_id, check_id); };
};

struct MemoryCheck {
    static std::atomic<size_t> fake_allocated_memory;
    bool b {false};
    bool operator()()
    {
        return true;
    }
    MemoryCheck(){};
    MemoryCheck(const MemoryCheck& x)
    {
        // We have to do this to make sure that destructor calls are paired
        //
        // Really, copy constructor should be deletable, but CCheckQueue breaks
        // if it is deleted because of internal push_back.
        fake_allocated_memory.fetch_add(b, std::memory_order_relaxed);
    };
    MemoryCheck(bool b_) : b(b_)
    {
        fake_allocated_memory.fetch_add(b, std::memory_order_relaxed);
    };
    ~MemoryCheck()
    {
        fake_allocated_memory.fetch_sub(b, std::memory_order_relaxed);
    };
    void swap(MemoryCheck& x) { std::swap(b, x.b); };
};

struct FrozenCleanupCheck {
    static std::atomic<uint64_t> nFrozen;
    static std::condition_variable cv;
    static std::mutex m;
    // Freezing can't be the default initialized behavior given how the queue
    // swaps in default initialized Checks.
    bool should_freeze {false};
    bool operator()()
    {
        return true;
    }
    FrozenCleanupCheck() {}
    ~FrozenCleanupCheck()
    {
        if (should_freeze) {
            std::unique_lock<std::mutex> l(m);
            nFrozen.store(1, std::memory_order_relaxed);
            cv.notify_one();
            cv.wait(l, []{ return nFrozen.load(std::memory_order_relaxed) == 0;});
        }
    }
    void swap(FrozenCleanupCheck& x){std::swap(should_freeze, x.should_freeze);};
};

// Static Allocations
std::mutex FrozenCleanupCheck::m{};
std::atomic<uint64_t> FrozenCleanupCheck::nFrozen{0};
std::condition_variable FrozenCleanupCheck::cv{};
std::mutex UniqueCheck::m;
std::unordered_multiset<size_t> UniqueCheck::results;
std::atomic<size_t> FakeCheckCheckCompletion::n_calls{0};
std::atomic<size_t> MemoryCheck::fake_allocated_memory{0};

// Queue Typedefs
typedef CCheckQueue<FakeCheckCheckCompletion> Correct_Queue;
typedef CCheckQueue<FakeCheck> Standard_Queue;
typedef CCheckQueue<FailingCheck> Failing_Queue;
typedef CCheckQueue<UniqueCheck> Unique_Queue;
typedef CCheckQueue<MemoryCheck> Memory_Queue;
typedef CCheckQueue<FrozenCleanupCheck> FrozenCleanup_Queue;

/** This test case checks that the CCheckQueue works properly
 * with each specified size_t Checks pushed.
 */
void Correct_Queue_range(std::vector<size_t> range)
{
    auto small_queue = std::unique_ptr<Correct_Queue>(new Correct_Queue {QUEUE_BATCH_SIZE});
    boost::thread_group tg;
    for (auto x = 0; x < nScriptCheckThreads; ++x) {
       tg.create_thread([&]{small_queue->Thread();});
    }
    // Make vChecks here to save on malloc (this test can be slow...)
    std::vector<FakeCheckCheckCompletion> vChecks;
    for (auto i : range) {
        size_t total = i;
        FakeCheckCheckCompletion::n_calls = 0;
        CCheckQueueControl<FakeCheckCheckCompletion> control(small_queue.get());
        while (total) {
            vChecks.resize(std::min(total, (size_t) InsecureRandRange(10)));
            total -= vChecks.size();
            control.Add(vChecks);
        }
        BOOST_REQUIRE(control.Wait());
        if (FakeCheckCheckCompletion::n_calls != i) {
            BOOST_REQUIRE_EQUAL(FakeCheckCheckCompletion::n_calls, i);
            BOOST_TEST_MESSAGE("Failure on trial " << i << " expected, got " << FakeCheckCheckCompletion::n_calls);
        }
    }
    tg.interrupt_all();
    tg.join_all();
}

/** Test that 0 checks is correct
 */
BOOST_AUTO_TEST_CASE(test_CheckQueue_Correct_Zero)
{
    std::vector<size_t> range;
    range.push_back((size_t)0);
    Correct_Queue_range(range);
}
/** Test that 1 check is correct
 */
BOOST_AUTO_TEST_CASE(test_CheckQueue_Correct_One)
{
    std::vector<size_t> range;
    range.push_back((size_t)1);
    Correct_Queue_range(range);
}
/** Test that MAX check is correct
 */
BOOST_AUTO_TEST_CASE(test_CheckQueue_Correct_Max)
{
    std::vector<size_t> range;
    range.push_back(100000);
    Correct_Queue_range(range);
}
/** Test that random numbers of checks are correct
 */
BOOST_AUTO_TEST_CASE(test_CheckQueue_Correct_Random)
{
    std::vector<size_t> range;
    range.reserve(100000/1000);
    for (size_t i = 2; i < 100000; i += std::max((size_t)1, (size_t)InsecureRandRange(std::min((size_t)1000, ((size_t)100000) - i))))
        range.push_back(i);
    Correct_Queue_range(range);
}

/** Test that failing checks are caught */
BOOST_AUTO_TEST_CASE(test_CheckQueue_Catches_Failure)
{
    auto fail_queue = std::unique_ptr<Failing_Queue>(new Failing_Queue {QUEUE_BATCH_SIZE});

    boost::thread_group tg;
    for (auto x = 0; x < nScriptCheckThreads; ++x) {
       tg.create_thread([&]{fail_queue->Thread();});
    }

    for (size_t i = 0; i < 1001; ++i) {
        CCheckQueueControl<FailingCheck> control(fail_queue.get());
        size_t remaining = i;
        while (remaining) {
            size_t r = InsecureRandRange(10);

            std::vector<FailingCheck> vChecks;
            vChecks.reserve(r);
            for (size_t k = 0; k < r && remaining; k++, remaining--)
                vChecks.emplace_back(remaining == 1);
            control.Add(vChecks);
        }
        bool success = control.Wait();
        if (i > 0) {
            BOOST_REQUIRE(!success);
        } else if (i == 0) {
            BOOST_REQUIRE(success);
        }
    }
    tg.interrupt_all();
    tg.join_all();
}
// Test that a block validation which fails does not interfere with
// future blocks, ie, the bad state is cleared.
BOOST_AUTO_TEST_CASE(test_CheckQueue_Recovers_From_Failure)
{
    auto fail_queue = std::unique_ptr<Failing_Queue>(new Failing_Queue {QUEUE_BATCH_SIZE});
    boost::thread_group tg;
    for (auto x = 0; x < nScriptCheckThreads; ++x) {
       tg.create_thread([&]{fail_queue->Thread();});
    }

    for (auto times = 0; times < 10; ++times) {
        for (bool end_fails : {true, false}) {
            CCheckQueueControl<FailingCheck> control(fail_queue.get());
            {
                std::vector<FailingCheck> vChecks;
                vChecks.resize(100, false);
                vChecks[99] = end_fails;
                control.Add(vChecks);
            }
            bool r =control.Wait();
            BOOST_REQUIRE(r != end_fails);
        }
    }
    tg.interrupt_all();
    tg.join_all();
}

// Test that unique checks are actually all called individually, rather than
// just one check being called repeatedly. Test that checks are not called
// more than once as well
BOOST_AUTO_TEST_CASE(test_CheckQueue_UniqueCheck)
{
    auto queue = std::unique_ptr<Unique_Queue>(new Unique_Queue {QUEUE_BATCH_SIZE});
    boost::thread_group tg;
    for (auto x = 0; x < nScriptCheckThreads; ++x) {
       tg.create_thread([&]{queue->Thread();});

    }

    size_t COUNT = 100000;
    size_t total = COUNT;
    {
        CCheckQueueControl<UniqueCheck> control(queue.get());
        while (total) {
            size_t r = InsecureRandRange(10);
            std::vector<UniqueCheck> vChecks;
            for (size_t k = 0; k < r && total; k++)
                vChecks.emplace_back(--total);
            control.Add(vChecks);
        }
    }
    bool r = true;
    BOOST_REQUIRE_EQUAL(UniqueCheck::results.size(), COUNT);
    for (size_t i = 0; i < COUNT; ++i)
        r = r && UniqueCheck::results.count(i) == 1;
    BOOST_REQUIRE(r);
    tg.interrupt_all();
    tg.join_all();
}

// Test that blocks which might allocate lots of memory free their memory aggressively.
//
// This test attempts to catch a pathological case where by lazily freeing
// checks might mean leaving a check un-swapped out, and decreasing by 1 each
// time could leave the data hanging across a sequence of blocks.
BOOST_AUTO_TEST_CASE(test_CheckQueue_Memory)
{
    auto queue = std::unique_ptr<Memory_Queue>(new Memory_Queue {QUEUE_BATCH_SIZE});
    boost::thread_group tg;
    for (auto x = 0; x < nScriptCheckThreads; ++x) {
       tg.create_thread([&]{queue->Thread();});
    }
    for (size_t i = 0; i < 1000; ++i) {
        size_t total = i;
        {
            CCheckQueueControl<MemoryCheck> control(queue.get());
            while (total) {
                size_t r = InsecureRandRange(10);
                std::vector<MemoryCheck> vChecks;
                for (size_t k = 0; k < r && total; k++) {
                    total--;
                    // Each iteration leaves data at the front, back, and middle
                    // to catch any sort of deallocation failure
                    vChecks.emplace_back(total == 0 || total == i || total == i/2);
                }
                control.Add(vChecks);
            }
        }
        BOOST_REQUIRE_EQUAL(MemoryCheck::fake_allocated_memory, 0);
    }
    tg.interrupt_all();
    tg.join_all();
}

// Test that a new verification cannot occur until all checks
// have been destructed
BOOST_AUTO_TEST_CASE(test_CheckQueue_FrozenCleanup)
{
    auto queue = std::unique_ptr<FrozenCleanup_Queue>(new FrozenCleanup_Queue {QUEUE_BATCH_SIZE});
    boost::thread_group tg;
    bool fails = false;
    for (auto x = 0; x < nScriptCheckThreads; ++x) {
        tg.create_thread([&]{queue->Thread();});
    }
    std::thread t0([&]() {
        CCheckQueueControl<FrozenCleanupCheck> control(queue.get());
        std::vector<FrozenCleanupCheck> vChecks(1);
        // Freezing can't be the default initialized behavior given how the queue
        // swaps in default initialized Checks (otherwise freezing destructor
        // would get called twice).
        vChecks[0].should_freeze = true;
        control.Add(vChecks);
        control.Wait(); // Hangs here
    });
    {
        std::unique_lock<std::mutex> l(FrozenCleanupCheck::m);
        // Wait until the queue has finished all jobs and frozen
        FrozenCleanupCheck::cv.wait(l, [](){return FrozenCleanupCheck::nFrozen == 1;});
    }
    // Try to get control of the queue a bunch of times
    for (auto x = 0; x < 100 && !fails; ++x) {
        fails = queue->ControlMutex.try_lock();
    }
    {
        // Unfreeze (we need lock n case of spurious wakeup)
        std::unique_lock<std::mutex> l(FrozenCleanupCheck::m);
        FrozenCleanupCheck::nFrozen = 0;
    }
    // Awaken frozen destructor
    FrozenCleanupCheck::cv.notify_one();
    // Wait for control to finish
    t0.join();
    tg.interrupt_all();
    tg.join_all();
    BOOST_REQUIRE(!fails);
}

/** Test that CCheckQueueControl is threadsafe */
BOOST_AUTO_TEST_CASE(test_CheckQueueControl_Locks)
{
    auto queue = std::unique_ptr<Standard_Queue>(new Standard_Queue{QUEUE_BATCH_SIZE});
    {
        boost::thread_group tg;
        std::atomic<int> nThreads {0};
        std::atomic<int> fails {0};
        for (size_t i = 0; i < 3; ++i) {
            tg.create_thread(
                    [&]{
                    CCheckQueueControl<FakeCheck> control(queue.get());
                    // While sleeping, no other thread should execute to this point
                    auto observed = ++nThreads;
                    MilliSleep(10);
                    fails += observed  != nThreads;
                    });
        }
        tg.join_all();
        BOOST_REQUIRE_EQUAL(fails, 0);
    }
    {
        boost::thread_group tg;
        std::mutex m;
        std::condition_variable cv;
        {
            bool has_lock {false};
            bool has_tried {false};
            bool done {false};
            bool done_ack {false};
            std::unique_lock<std::mutex> l(m);
            tg.create_thread([&]{
                    CCheckQueueControl<FakeCheck> control(queue.get());
                    std::unique_lock<std::mutex> ll(m);
                    has_lock = true;
                    cv.notify_one();
                    cv.wait(ll, [&]{return has_tried;});
                    done = true;
                    cv.notify_one();
                    // Wait until the done is acknowledged
                    //
                    cv.wait(ll, [&]{return done_ack;});
                    });
            // Wait for thread to get the lock
            cv.wait(l, [&](){return has_lock;});
            bool fails = false;
            for (auto x = 0; x < 100 && !fails; ++x) {
                fails = queue->ControlMutex.try_lock();
            }
            has_tried = true;
            cv.notify_one();
            cv.wait(l, [&](){return done;});
            // Acknowledge the done
            done_ack = true;
            cv.notify_one();
            BOOST_REQUIRE(!fails);
        }
        tg.join_all();
    }
}
BOOST_AUTO_TEST_SUITE_END()

<|endoftext|>