<commit_before>/* Copyright 2020 The TensorFlow Quantum Authors. All Rights Reserved.

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 <stdlib.h>

#include <string>

#include "../qsim/lib/channel.h"
#include "../qsim/lib/channels_cirq.h"
#include "../qsim/lib/circuit.h"
#include "../qsim/lib/circuit_noisy.h"
#include "../qsim/lib/fuser_mqubit.h"
#include "../qsim/lib/gate_appl.h"
#include "../qsim/lib/gates_cirq.h"
#include "../qsim/lib/io.h"
#include "../qsim/lib/qtrajectory.h"
#include "../qsim/lib/seqfor.h"
#include "../qsim/lib/simmux.h"
#include "tensorflow/core/framework/op_kernel.h"
#include "tensorflow/core/framework/shape_inference.h"
#include "tensorflow/core/framework/tensor_shape.h"
#include "tensorflow/core/lib/core/error_codes.pb.h"
#include "tensorflow/core/lib/core/status.h"
#include "tensorflow/core/lib/core/threadpool.h"
#include "tensorflow/core/lib/random/random.h"
#include "tensorflow/core/lib/random/simple_philox.h"
#include "tensorflow/core/util/guarded_philox_random.h"
#include "tensorflow_quantum/core/ops/parse_context.h"
#include "tensorflow_quantum/core/proto/program.pb.h"
#include "tensorflow_quantum/core/src/circuit_parser_qsim.h"
#include "tensorflow_quantum/core/src/util_qsim.h"

namespace tfq {

using ::tensorflow::Status;
using ::tfq::proto::Program;

typedef qsim::Cirq::GateCirq<float> QsimGate;
typedef qsim::Circuit<QsimGate> QsimCircuit;
typedef qsim::NoisyCircuit<QsimGate> NoisyQsimCircuit;

class TfqNoisySamplesOp : public tensorflow::OpKernel {
 public:
  explicit TfqNoisySamplesOp(tensorflow::OpKernelConstruction* context)
      : OpKernel(context) {}

  void Compute(tensorflow::OpKernelContext* context) override {
    // TODO (mbbrough): add more dimension checks for other inputs here.
    DCHECK_EQ(4, context->num_inputs());

    // Parse to Program Proto and num_qubits.
    std::vector<Program> programs;
    std::vector<int> num_qubits;
    OP_REQUIRES_OK(context,
                   GetProgramsAndNumQubits(context, &programs, &num_qubits));

    // Parse symbol maps for parameter resolution in the circuits.
    std::vector<SymbolMap> maps;
    OP_REQUIRES_OK(context, GetSymbolMaps(context, &maps));
    OP_REQUIRES(
        context, maps.size() == programs.size(),
        tensorflow::errors::InvalidArgument(absl::StrCat(
            "Number of circuits and values do not match. Got ", programs.size(),
            " circuits and ", maps.size(), " values.")));

    int num_samples = 0;
    OP_REQUIRES_OK(context, GetIndividualSample(context, &num_samples));

    // Construct qsim circuits.
    std::vector<NoisyQsimCircuit> qsim_circuits(programs.size(),
                                                NoisyQsimCircuit());

    Status parse_status = Status::OK();
    auto p_lock = tensorflow::mutex();
    auto construct_f = [&](int start, int end) {
      for (int i = start; i < end; i++) {
        auto r = NoisyQsimCircuitFromProgram(
            programs[i], maps[i], num_qubits[i], true, &qsim_circuits[i]);
        NESTED_FN_STATUS_SYNC(parse_status, r, p_lock);
      }
    };

    const int num_cycles = 1000;
    context->device()->tensorflow_cpu_worker_threads()->workers->ParallelFor(
        programs.size(), num_cycles, construct_f);
    OP_REQUIRES_OK(context, parse_status);

    int max_num_qubits = 0;
    for (const int num : num_qubits) {
      max_num_qubits = std::max(max_num_qubits, num);
    }

    const int output_dim_size = maps.size();
    tensorflow::TensorShape output_shape;
    output_shape.AddDim(output_dim_size);
    output_shape.AddDim(num_samples);
    output_shape.AddDim(max_num_qubits);

    tensorflow::Tensor* output = nullptr;
    OP_REQUIRES_OK(context, context->allocate_output(0, output_shape, &output));
    auto output_tensor = output->tensor<int8_t, 3>();

    if (num_samples == 0 || output_dim_size == 0 || max_num_qubits == 0) {
      return;  // bug in qsim dependency we can't control.
    }

    // Cross reference with standard google cloud compute instances
    // Memory ~= 2 * num_threads * (2 * 64 * 2 ** num_qubits in circuits)
    // e2s2 = 2 CPU, 8GB -> Can safely do 25 since Memory = 4GB
    // e2s4 = 4 CPU, 16GB -> Can safely do 25 since Memory = 8GB
    // ...
    if (max_num_qubits >= 26) {
      ComputeLarge(num_qubits, max_num_qubits, num_samples, qsim_circuits,
                   context, &output_tensor);
    } else {
      ComputeSmall(num_qubits, max_num_qubits, num_samples, qsim_circuits,
                   context, &output_tensor);
    }
  }

 private:
  void ComputeLarge(const std::vector<int>& num_qubits,
                    const int max_num_qubits, const int num_samples,
                    const std::vector<NoisyQsimCircuit>& ncircuits,
                    tensorflow::OpKernelContext* context,
                    tensorflow::TTypes<int8_t, 3>::Tensor* output_tensor) {
    // Instantiate qsim objects.
    const auto tfq_for = tfq::QsimFor(context);
    using Simulator = qsim::Simulator<const tfq::QsimFor&>;
    using StateSpace = Simulator::StateSpace;
    using QTSimulator =
        qsim::QuantumTrajectorySimulator<qsim::IO, QsimGate,
                                         qsim::MultiQubitGateFuser, Simulator>;

    // Begin simulation.
    int largest_nq = 1;
    Simulator sim = Simulator(tfq_for);
    StateSpace ss = StateSpace(tfq_for);
    auto sv = ss.Create(largest_nq);

    tensorflow::GuardedPhiloxRandom random_gen;
    random_gen.Init(tensorflow::random::New64(), tensorflow::random::New64());
    auto local_gen =
        random_gen.ReserveSamples32(2 * num_samples * ncircuits.size() + 2);
    tensorflow::random::SimplePhilox rand_source(&local_gen);

    // Simulate programs one by one. Parallelizing over state vectors
    // we no longer parallelize over circuits. Each time we encounter a
    // a larger circuit we will grow the Statevector as nescessary.
    for (int i = 0; i < ncircuits.size(); i++) {
      int nq = num_qubits[i];

      if (nq > largest_nq) {
        // need to switch to larger statespace.
        largest_nq = nq;
        sv = ss.Create(largest_nq);
      }

      QTSimulator::Parameter param;
      param.collect_kop_stat = false;
      param.collect_mea_stat = true;
      param.normalize_before_mea_gates = true;

      // Track op-wise stats.
      QTSimulator::Stat gathered_samples;
      //std::vector<uint64_t> gathered_samples;

      for (int j = 0; j < num_samples; j++) {
        ss.SetStateZero(sv);

        QTSimulator::RunOnce(param, ncircuits[i], rand_source.Rand64(), ss, sim,
                             sv, gathered_samples);
        uint64_t q_ind = 0;
        uint64_t mask = 1;
        bool val = 0;
        while (q_ind < nq) {
          val = gathered_samples.samples[0] & mask;
          (*output_tensor)(
              i, j, static_cast<ptrdiff_t>(max_num_qubits - q_ind - 1)) = val;
          q_ind++;
          mask <<= 1;
        }
        while (q_ind < max_num_qubits) {
          (*output_tensor)(
              i, j, static_cast<ptrdiff_t>(max_num_qubits - q_ind - 1)) = -2;
          q_ind++;
        }
      }
    }
  }

  void ComputeSmall(const std::vector<int>& num_qubits,
                    const int max_num_qubits, const int num_samples,
                    const std::vector<NoisyQsimCircuit>& ncircuits,
                    tensorflow::OpKernelContext* context,
                    tensorflow::TTypes<int8_t, 3>::Tensor* output_tensor) {
    using Simulator = qsim::Simulator<const qsim::SequentialFor&>;
    using StateSpace = Simulator::StateSpace;
    using QTSimulator =
        qsim::QuantumTrajectorySimulator<qsim::IO, QsimGate,
                                         qsim::MultiQubitGateFuser, Simulator>;

    const int output_dim_batch_size = output_tensor->dimension(0);
    const int num_threads = context->device()
                                ->tensorflow_cpu_worker_threads()
                                ->workers->NumThreads();

    // [num_threads, batch_size].
    std::vector<std::vector<int>> rep_offsets(
        num_threads, std::vector<int>(output_dim_batch_size, 0));
    BalanceTrajectory(num_samples, num_threads, &rep_offsets);

    // [num_threads, batch_size] stores the number of
    // samples written by thread range [0, i].
    std::vector<std::vector<long>> offset_prefix_sum(
        num_threads, std::vector<long>(output_dim_batch_size, 0));

    for (int i = 0; i < output_dim_batch_size; i++) {
      int p_reps = (num_samples + num_threads - 1) / num_threads;
      offset_prefix_sum[0][i] = rep_offsets[0][i] + p_reps;
      for (int j = 1; j < num_threads; j++) {
        offset_prefix_sum[j][i] += offset_prefix_sum[j - 1][i];
        offset_prefix_sum[j][i] += rep_offsets[j][i] + p_reps;
      }
    }

    tensorflow::GuardedPhiloxRandom random_gen;
    random_gen.Init(tensorflow::random::New64(), tensorflow::random::New64());

    auto DoWork = [&](int start, int end) {
      // Begin simulation.
      const auto tfq_for = qsim::SequentialFor(1);
      int largest_nq = 1;
      Simulator sim = Simulator(tfq_for);
      StateSpace ss = StateSpace(tfq_for);
      auto sv = ss.Create(largest_nq);

      int needed_random =
          4 * (num_samples * ncircuits.size() + num_threads) / num_threads;
      needed_random += 4;
      auto local_gen = random_gen.ReserveSamples32(needed_random);
      tensorflow::random::SimplePhilox rand_source(&local_gen);

      for (int i = 0; i < ncircuits.size(); i++) {
        int nq = num_qubits[i];
        int j = start > 0 ? offset_prefix_sum[start - 1][i] : 0;
        int needed_samples = offset_prefix_sum[start][i] - j;
        if (needed_samples <= 0) {
          continue;
        }

        if (nq > largest_nq) {
          largest_nq = nq;
          sv = ss.Create(largest_nq);
        }
        QTSimulator::Parameter param;
        param.collect_kop_stat = false;
        param.collect_mea_stat = true;
        param.normalize_before_mea_gates = true;

        // Track op-wise stats.
        QTSimulator::Stat gathered_samples;
        int run_samples = 0;

        while (1) {
          ss.SetStateZero(sv);
          QTSimulator::RunOnce(param, ncircuits[i], rand_source.Rand64(), ss,
                               sim, sv, gathered_samples);

          uint64_t q_ind = 0;
          uint64_t mask = 1;
          bool val = 0;
          while (q_ind < nq) {
            val = gathered_samples.samples[0] & mask;
            (*output_tensor)(
                i, j, static_cast<ptrdiff_t>(max_num_qubits - q_ind - 1)) = val;
            q_ind++;
            mask <<= 1;
          }
          while (q_ind < max_num_qubits) {
            (*output_tensor)(
                i, j, static_cast<ptrdiff_t>(max_num_qubits - q_ind - 1)) = -2;
            q_ind++;
          }

          j++;
          run_samples++;

          // Check if we have gathered enough samples.
          if (run_samples >= needed_samples) {
            break;
          }
        }
      }
    };

    // block_size = 1.
    tensorflow::thread::ThreadPool::SchedulingParams scheduling_params(
        tensorflow::thread::ThreadPool::SchedulingStrategy::kFixedBlockSize,
        absl::nullopt, 1);
    context->device()->tensorflow_cpu_worker_threads()->workers->ParallelFor(
        num_threads, scheduling_params, DoWork);
  }
};

REGISTER_KERNEL_BUILDER(Name("TfqNoisySamples").Device(tensorflow::DEVICE_CPU),
                        TfqNoisySamplesOp);

REGISTER_OP("TfqNoisySamples")
    .Input("programs: string")
    .Input("symbol_names: string")
    .Input("symbol_values: float")
    .Input("num_samples: int32")
    .Output("samples: int8")
    .SetShapeFn([](tensorflow::shape_inference::InferenceContext* c) {
      tensorflow::shape_inference::ShapeHandle programs_shape;
      TF_RETURN_IF_ERROR(c->WithRank(c->input(0), 1, &programs_shape));

      tensorflow::shape_inference::ShapeHandle symbol_names_shape;
      TF_RETURN_IF_ERROR(c->WithRank(c->input(1), 1, &symbol_names_shape));

      tensorflow::shape_inference::ShapeHandle symbol_values_shape;
      TF_RETURN_IF_ERROR(c->WithRank(c->input(2), 2, &symbol_values_shape));

      tensorflow::shape_inference::ShapeHandle num_samples_shape;
      TF_RETURN_IF_ERROR(c->WithRank(c->input(3), 1, &num_samples_shape));

      // [batch_size, n_samples, largest_n_qubits]
      c->set_output(
          0, c->MakeShape(
                 {c->Dim(programs_shape, 0),
                  tensorflow::shape_inference::InferenceContext::kUnknownDim,
                  tensorflow::shape_inference::InferenceContext::kUnknownDim}));

      return tensorflow::Status::OK();
    });

}  // namespace tfq
<commit_msg>format.<commit_after>/* Copyright 2020 The TensorFlow Quantum Authors. All Rights Reserved.

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 <stdlib.h>

#include <string>

#include "../qsim/lib/channel.h"
#include "../qsim/lib/channels_cirq.h"
#include "../qsim/lib/circuit.h"
#include "../qsim/lib/circuit_noisy.h"
#include "../qsim/lib/fuser_mqubit.h"
#include "../qsim/lib/gate_appl.h"
#include "../qsim/lib/gates_cirq.h"
#include "../qsim/lib/io.h"
#include "../qsim/lib/qtrajectory.h"
#include "../qsim/lib/seqfor.h"
#include "../qsim/lib/simmux.h"
#include "tensorflow/core/framework/op_kernel.h"
#include "tensorflow/core/framework/shape_inference.h"
#include "tensorflow/core/framework/tensor_shape.h"
#include "tensorflow/core/lib/core/error_codes.pb.h"
#include "tensorflow/core/lib/core/status.h"
#include "tensorflow/core/lib/core/threadpool.h"
#include "tensorflow/core/lib/random/random.h"
#include "tensorflow/core/lib/random/simple_philox.h"
#include "tensorflow/core/util/guarded_philox_random.h"
#include "tensorflow_quantum/core/ops/parse_context.h"
#include "tensorflow_quantum/core/proto/program.pb.h"
#include "tensorflow_quantum/core/src/circuit_parser_qsim.h"
#include "tensorflow_quantum/core/src/util_qsim.h"

namespace tfq {

using ::tensorflow::Status;
using ::tfq::proto::Program;

typedef qsim::Cirq::GateCirq<float> QsimGate;
typedef qsim::Circuit<QsimGate> QsimCircuit;
typedef qsim::NoisyCircuit<QsimGate> NoisyQsimCircuit;

class TfqNoisySamplesOp : public tensorflow::OpKernel {
 public:
  explicit TfqNoisySamplesOp(tensorflow::OpKernelConstruction* context)
      : OpKernel(context) {}

  void Compute(tensorflow::OpKernelContext* context) override {
    // TODO (mbbrough): add more dimension checks for other inputs here.
    DCHECK_EQ(4, context->num_inputs());

    // Parse to Program Proto and num_qubits.
    std::vector<Program> programs;
    std::vector<int> num_qubits;
    OP_REQUIRES_OK(context,
                   GetProgramsAndNumQubits(context, &programs, &num_qubits));

    // Parse symbol maps for parameter resolution in the circuits.
    std::vector<SymbolMap> maps;
    OP_REQUIRES_OK(context, GetSymbolMaps(context, &maps));
    OP_REQUIRES(
        context, maps.size() == programs.size(),
        tensorflow::errors::InvalidArgument(absl::StrCat(
            "Number of circuits and values do not match. Got ", programs.size(),
            " circuits and ", maps.size(), " values.")));

    int num_samples = 0;
    OP_REQUIRES_OK(context, GetIndividualSample(context, &num_samples));

    // Construct qsim circuits.
    std::vector<NoisyQsimCircuit> qsim_circuits(programs.size(),
                                                NoisyQsimCircuit());

    Status parse_status = Status::OK();
    auto p_lock = tensorflow::mutex();
    auto construct_f = [&](int start, int end) {
      for (int i = start; i < end; i++) {
        auto r = NoisyQsimCircuitFromProgram(
            programs[i], maps[i], num_qubits[i], true, &qsim_circuits[i]);
        NESTED_FN_STATUS_SYNC(parse_status, r, p_lock);
      }
    };

    const int num_cycles = 1000;
    context->device()->tensorflow_cpu_worker_threads()->workers->ParallelFor(
        programs.size(), num_cycles, construct_f);
    OP_REQUIRES_OK(context, parse_status);

    int max_num_qubits = 0;
    for (const int num : num_qubits) {
      max_num_qubits = std::max(max_num_qubits, num);
    }

    const int output_dim_size = maps.size();
    tensorflow::TensorShape output_shape;
    output_shape.AddDim(output_dim_size);
    output_shape.AddDim(num_samples);
    output_shape.AddDim(max_num_qubits);

    tensorflow::Tensor* output = nullptr;
    OP_REQUIRES_OK(context, context->allocate_output(0, output_shape, &output));
    auto output_tensor = output->tensor<int8_t, 3>();

    if (num_samples == 0 || output_dim_size == 0 || max_num_qubits == 0) {
      return;  // bug in qsim dependency we can't control.
    }

    // Cross reference with standard google cloud compute instances
    // Memory ~= 2 * num_threads * (2 * 64 * 2 ** num_qubits in circuits)
    // e2s2 = 2 CPU, 8GB -> Can safely do 25 since Memory = 4GB
    // e2s4 = 4 CPU, 16GB -> Can safely do 25 since Memory = 8GB
    // ...
    if (max_num_qubits >= 26) {
      ComputeLarge(num_qubits, max_num_qubits, num_samples, qsim_circuits,
                   context, &output_tensor);
    } else {
      ComputeSmall(num_qubits, max_num_qubits, num_samples, qsim_circuits,
                   context, &output_tensor);
    }
  }

 private:
  void ComputeLarge(const std::vector<int>& num_qubits,
                    const int max_num_qubits, const int num_samples,
                    const std::vector<NoisyQsimCircuit>& ncircuits,
                    tensorflow::OpKernelContext* context,
                    tensorflow::TTypes<int8_t, 3>::Tensor* output_tensor) {
    // Instantiate qsim objects.
    const auto tfq_for = tfq::QsimFor(context);
    using Simulator = qsim::Simulator<const tfq::QsimFor&>;
    using StateSpace = Simulator::StateSpace;
    using QTSimulator =
        qsim::QuantumTrajectorySimulator<qsim::IO, QsimGate,
                                         qsim::MultiQubitGateFuser, Simulator>;

    // Begin simulation.
    int largest_nq = 1;
    Simulator sim = Simulator(tfq_for);
    StateSpace ss = StateSpace(tfq_for);
    auto sv = ss.Create(largest_nq);

    tensorflow::GuardedPhiloxRandom random_gen;
    random_gen.Init(tensorflow::random::New64(), tensorflow::random::New64());
    auto local_gen =
        random_gen.ReserveSamples32(2 * num_samples * ncircuits.size() + 2);
    tensorflow::random::SimplePhilox rand_source(&local_gen);

    // Simulate programs one by one. Parallelizing over state vectors
    // we no longer parallelize over circuits. Each time we encounter a
    // a larger circuit we will grow the Statevector as nescessary.
    for (int i = 0; i < ncircuits.size(); i++) {
      int nq = num_qubits[i];

      if (nq > largest_nq) {
        // need to switch to larger statespace.
        largest_nq = nq;
        sv = ss.Create(largest_nq);
      }

      QTSimulator::Parameter param;
      param.collect_kop_stat = false;
      param.collect_mea_stat = true;
      param.normalize_before_mea_gates = true;

      // Track op-wise stats.
      QTSimulator::Stat gathered_samples;

      for (int j = 0; j < num_samples; j++) {
        ss.SetStateZero(sv);

        QTSimulator::RunOnce(param, ncircuits[i], rand_source.Rand64(), ss, sim,
                             sv, gathered_samples);
        uint64_t q_ind = 0;
        uint64_t mask = 1;
        bool val = 0;
        while (q_ind < nq) {
          val = gathered_samples.samples[0] & mask;
          (*output_tensor)(
              i, j, static_cast<ptrdiff_t>(max_num_qubits - q_ind - 1)) = val;
          q_ind++;
          mask <<= 1;
        }
        while (q_ind < max_num_qubits) {
          (*output_tensor)(
              i, j, static_cast<ptrdiff_t>(max_num_qubits - q_ind - 1)) = -2;
          q_ind++;
        }
      }
    }
  }

  void ComputeSmall(const std::vector<int>& num_qubits,
                    const int max_num_qubits, const int num_samples,
                    const std::vector<NoisyQsimCircuit>& ncircuits,
                    tensorflow::OpKernelContext* context,
                    tensorflow::TTypes<int8_t, 3>::Tensor* output_tensor) {
    using Simulator = qsim::Simulator<const qsim::SequentialFor&>;
    using StateSpace = Simulator::StateSpace;
    using QTSimulator =
        qsim::QuantumTrajectorySimulator<qsim::IO, QsimGate,
                                         qsim::MultiQubitGateFuser, Simulator>;

    const int output_dim_batch_size = output_tensor->dimension(0);
    const int num_threads = context->device()
                                ->tensorflow_cpu_worker_threads()
                                ->workers->NumThreads();

    // [num_threads, batch_size].
    std::vector<std::vector<int>> rep_offsets(
        num_threads, std::vector<int>(output_dim_batch_size, 0));
    BalanceTrajectory(num_samples, num_threads, &rep_offsets);

    // [num_threads, batch_size] stores the number of
    // samples written by thread range [0, i].
    std::vector<std::vector<long>> offset_prefix_sum(
        num_threads, std::vector<long>(output_dim_batch_size, 0));

    for (int i = 0; i < output_dim_batch_size; i++) {
      int p_reps = (num_samples + num_threads - 1) / num_threads;
      offset_prefix_sum[0][i] = rep_offsets[0][i] + p_reps;
      for (int j = 1; j < num_threads; j++) {
        offset_prefix_sum[j][i] += offset_prefix_sum[j - 1][i];
        offset_prefix_sum[j][i] += rep_offsets[j][i] + p_reps;
      }
    }

    tensorflow::GuardedPhiloxRandom random_gen;
    random_gen.Init(tensorflow::random::New64(), tensorflow::random::New64());

    auto DoWork = [&](int start, int end) {
      // Begin simulation.
      const auto tfq_for = qsim::SequentialFor(1);
      int largest_nq = 1;
      Simulator sim = Simulator(tfq_for);
      StateSpace ss = StateSpace(tfq_for);
      auto sv = ss.Create(largest_nq);

      int needed_random =
          4 * (num_samples * ncircuits.size() + num_threads) / num_threads;
      needed_random += 4;
      auto local_gen = random_gen.ReserveSamples32(needed_random);
      tensorflow::random::SimplePhilox rand_source(&local_gen);

      for (int i = 0; i < ncircuits.size(); i++) {
        int nq = num_qubits[i];
        int j = start > 0 ? offset_prefix_sum[start - 1][i] : 0;
        int needed_samples = offset_prefix_sum[start][i] - j;
        if (needed_samples <= 0) {
          continue;
        }

        if (nq > largest_nq) {
          largest_nq = nq;
          sv = ss.Create(largest_nq);
        }
        QTSimulator::Parameter param;
        param.collect_kop_stat = false;
        param.collect_mea_stat = true;
        param.normalize_before_mea_gates = true;

        // Track op-wise stats.
        QTSimulator::Stat gathered_samples;
        int run_samples = 0;

        while (1) {
          ss.SetStateZero(sv);
          QTSimulator::RunOnce(param, ncircuits[i], rand_source.Rand64(), ss,
                               sim, sv, gathered_samples);

          uint64_t q_ind = 0;
          uint64_t mask = 1;
          bool val = 0;
          while (q_ind < nq) {
            val = gathered_samples.samples[0] & mask;
            (*output_tensor)(
                i, j, static_cast<ptrdiff_t>(max_num_qubits - q_ind - 1)) = val;
            q_ind++;
            mask <<= 1;
          }
          while (q_ind < max_num_qubits) {
            (*output_tensor)(
                i, j, static_cast<ptrdiff_t>(max_num_qubits - q_ind - 1)) = -2;
            q_ind++;
          }

          j++;
          run_samples++;

          // Check if we have gathered enough samples.
          if (run_samples >= needed_samples) {
            break;
          }
        }
      }
    };

    // block_size = 1.
    tensorflow::thread::ThreadPool::SchedulingParams scheduling_params(
        tensorflow::thread::ThreadPool::SchedulingStrategy::kFixedBlockSize,
        absl::nullopt, 1);
    context->device()->tensorflow_cpu_worker_threads()->workers->ParallelFor(
        num_threads, scheduling_params, DoWork);
  }
};

REGISTER_KERNEL_BUILDER(Name("TfqNoisySamples").Device(tensorflow::DEVICE_CPU),
                        TfqNoisySamplesOp);

REGISTER_OP("TfqNoisySamples")
    .Input("programs: string")
    .Input("symbol_names: string")
    .Input("symbol_values: float")
    .Input("num_samples: int32")
    .Output("samples: int8")
    .SetShapeFn([](tensorflow::shape_inference::InferenceContext* c) {
      tensorflow::shape_inference::ShapeHandle programs_shape;
      TF_RETURN_IF_ERROR(c->WithRank(c->input(0), 1, &programs_shape));

      tensorflow::shape_inference::ShapeHandle symbol_names_shape;
      TF_RETURN_IF_ERROR(c->WithRank(c->input(1), 1, &symbol_names_shape));

      tensorflow::shape_inference::ShapeHandle symbol_values_shape;
      TF_RETURN_IF_ERROR(c->WithRank(c->input(2), 2, &symbol_values_shape));

      tensorflow::shape_inference::ShapeHandle num_samples_shape;
      TF_RETURN_IF_ERROR(c->WithRank(c->input(3), 1, &num_samples_shape));

      // [batch_size, n_samples, largest_n_qubits]
      c->set_output(
          0, c->MakeShape(
                 {c->Dim(programs_shape, 0),
                  tensorflow::shape_inference::InferenceContext::kUnknownDim,
                  tensorflow::shape_inference::InferenceContext::kUnknownDim}));

      return tensorflow::Status::OK();
    });

}  // namespace tfq
<|endoftext|>


<commit_before>//RUN: %clang_cc1 %s -emit-llvm -o - -triple=i686-unknown-linux | FileCheck --check-prefix=CHECKGEN %s
//RUN: %clang_cc1 %s -emit-llvm -o - -triple=thumbv7-apple-ios3.0 -target-abi apcs-gnu | FileCheck --check-prefix=CHECKARM %s
//RUN: not %clang_cc1 %s -emit-llvm -o - -DPR12784_WORKAROUND -triple=x86_64-pc-win32 -cxx-abi microsoft | FileCheck --check-prefix=CHECKMS %s

// FIXME: Add checks to ensure that Microsoft destructors do not return 'this'
// once PR12784 is resolved

// Make sure we attach the 'returned' attribute to the 'this' parameter of
// constructors and destructors which return this (and only these cases)

class A {
public:
  A();
  ~A();

private:
  int x_;
};

class B : public A {
public:
  B(int *i);
  ~B();

private:
  int *i_;
};

B::B(int *i) : i_(i) { }
#ifndef PR12784_WORKAROUND
B::~B() { }
#endif

// CHECKGEN-LABEL: define void @_ZN1BC1EPi(%class.B* %this, i32* %i)
// CHECKGEN-LABEL: define void @_ZN1BC2EPi(%class.B* %this, i32* %i)
// CHECKGEN-LABEL: define void @_ZN1BD1Ev(%class.B* %this)
// CHECKGEN-LABEL: define void @_ZN1BD2Ev(%class.B* %this)

// CHECKARM-LABEL: define %class.B* @_ZN1BC1EPi(%class.B* returned %this, i32* %i)
// CHECKARM-LABEL: define %class.B* @_ZN1BC2EPi(%class.B* returned %this, i32* %i)
// CHECKARM-LABEL: define %class.B* @_ZN1BD1Ev(%class.B* returned %this)
// CHECKARM-LABEL: define %class.B* @_ZN1BD2Ev(%class.B* returned %this)

// CHECKMS: define %class.B* @"\01??0B@@QEAA@PEAH@Z"(%class.B* returned %this, i32* %i)

class C : public A, public B {
public:
  C(int *i, char *c);
  virtual ~C();
private:
  char *c_;
};

C::C(int *i, char *c) : B(i), c_(c) { }
#ifndef PR12784_WORKAROUND
C::~C() { }
#endif

// CHECKGEN-LABEL: define void @_ZN1CC1EPiPc(%class.C* %this, i32* %i, i8* %c)
// CHECKGEN-LABEL: define void @_ZN1CC2EPiPc(%class.C* %this, i32* %i, i8* %c)
// CHECKGEN-LABEL: define void @_ZN1CD0Ev(%class.C* %this)
// CHECKGEN-LABEL: define void @_ZN1CD1Ev(%class.C* %this)
// CHECKGEN-LABEL: define void @_ZN1CD2Ev(%class.C* %this)

// CHECKARM-LABEL: define %class.C* @_ZN1CC1EPiPc(%class.C* returned %this, i32* %i, i8* %c)
// CHECKARM-LABEL: define %class.C* @_ZN1CC2EPiPc(%class.C* returned %this, i32* %i, i8* %c)
// CHECKARM-LABEL: define void @_ZN1CD0Ev(%class.C* %this)
// CHECKARM-LABEL: define %class.C* @_ZN1CD1Ev(%class.C* returned %this)
// CHECKARM-LABEL: define %class.C* @_ZN1CD2Ev(%class.C* returned %this)

// CHECKMS: define %class.C* @"\01??0C@@QEAA@PEAHPEAD@Z"(%class.C* returned %this, i32* %i, i8* %c)

class D : public virtual A {
public:
  D();
  ~D();
};

#ifndef PR12784_WORKAROUND
D::D() { }
D::~D() { }
#endif

// CHECKGEN-LABEL: define void @_ZN1DC1Ev(%class.D* %this)
// CHECKGEN-LABEL: define void @_ZN1DC2Ev(%class.D* %this, i8** %vtt)
// CHECKGEN-LABEL: define void @_ZN1DD1Ev(%class.D* %this)
// CHECKGEN-LABEL: define void @_ZN1DD2Ev(%class.D* %this, i8** %vtt)

// CHECKARM-LABEL: define %class.D* @_ZN1DC1Ev(%class.D* returned %this)
// CHECKARM-LABEL: define %class.D* @_ZN1DC2Ev(%class.D* returned %this, i8** %vtt)
// CHECKARM-LABEL: define %class.D* @_ZN1DD1Ev(%class.D* returned %this)
// CHECKARM-LABEL: define %class.D* @_ZN1DD2Ev(%class.D* returned %this, i8** %vtt)

class E {
public:
  E();
  virtual ~E();
};

E* gete();

void test_destructor() {
  const E& e1 = E();
  E* e2 = gete();
  e2->~E();
}

// CHECKARM-LABEL: define void @_Z15test_destructorv()

// Verify that virtual calls to destructors are not marked with a 'returned'
// this parameter at the call site...
// CHECKARM: [[VFN:%.*]] = getelementptr inbounds %class.E* (%class.E*)**
// CHECKARM: [[THUNK:%.*]] = load %class.E* (%class.E*)** [[VFN]]
// CHECKARM: call %class.E* [[THUNK]](%class.E* %

// ...but static calls create declarations with 'returned' this
// CHECKARM: {{%.*}} = call %class.E* @_ZN1ED1Ev(%class.E* %

// CHECKARM: declare %class.E* @_ZN1ED1Ev(%class.E* returned)
<commit_msg>Author a FIXME in a test now that PR12784 is fixed<commit_after>//RUN: %clang_cc1 %s -emit-llvm -o - -triple=i686-unknown-linux | FileCheck --check-prefix=CHECKGEN %s
//RUN: %clang_cc1 %s -emit-llvm -o - -triple=thumbv7-apple-ios3.0 -target-abi apcs-gnu | FileCheck --check-prefix=CHECKARM %s
//RUN: %clang_cc1 %s -emit-llvm -o - -triple=x86_64-pc-win32 -cxx-abi microsoft -fno-rtti | FileCheck --check-prefix=CHECKMS %s

// Make sure we attach the 'returned' attribute to the 'this' parameter of
// constructors and destructors which return this (and only these cases)

class A {
public:
  A();
  ~A();

private:
  int x_;
};

class B : public A {
public:
  B(int *i);
  ~B();

private:
  int *i_;
};

B::B(int *i) : i_(i) { }
B::~B() { }

// CHECKGEN-LABEL: define void @_ZN1BC1EPi(%class.B* %this, i32* %i)
// CHECKGEN-LABEL: define void @_ZN1BC2EPi(%class.B* %this, i32* %i)
// CHECKGEN-LABEL: define void @_ZN1BD1Ev(%class.B* %this)
// CHECKGEN-LABEL: define void @_ZN1BD2Ev(%class.B* %this)

// CHECKARM-LABEL: define %class.B* @_ZN1BC1EPi(%class.B* returned %this, i32* %i)
// CHECKARM-LABEL: define %class.B* @_ZN1BC2EPi(%class.B* returned %this, i32* %i)
// CHECKARM-LABEL: define %class.B* @_ZN1BD1Ev(%class.B* returned %this)
// CHECKARM-LABEL: define %class.B* @_ZN1BD2Ev(%class.B* returned %this)

// CHECKMS-LABEL: define %class.B* @"\01??0B@@QEAA@PEAH@Z"(%class.B* returned %this, i32* %i)
// CHECKMS-LABEL: define void @"\01??1B@@QEAA@XZ"(%class.B* %this)

class C : public A, public B {
public:
  C(int *i, char *c);
  virtual ~C();
private:
  char *c_;
};

C::C(int *i, char *c) : B(i), c_(c) { }
C::~C() { }

// CHECKGEN-LABEL: define void @_ZN1CC1EPiPc(%class.C* %this, i32* %i, i8* %c)
// CHECKGEN-LABEL: define void @_ZN1CC2EPiPc(%class.C* %this, i32* %i, i8* %c)
// CHECKGEN-LABEL: define void @_ZN1CD0Ev(%class.C* %this)
// CHECKGEN-LABEL: define void @_ZN1CD1Ev(%class.C* %this)
// CHECKGEN-LABEL: define void @_ZN1CD2Ev(%class.C* %this)

// CHECKARM-LABEL: define %class.C* @_ZN1CC1EPiPc(%class.C* returned %this, i32* %i, i8* %c)
// CHECKARM-LABEL: define %class.C* @_ZN1CC2EPiPc(%class.C* returned %this, i32* %i, i8* %c)
// CHECKARM-LABEL: define void @_ZN1CD0Ev(%class.C* %this)
// CHECKARM-LABEL: define %class.C* @_ZN1CD1Ev(%class.C* returned %this)
// CHECKARM-LABEL: define %class.C* @_ZN1CD2Ev(%class.C* returned %this)

// CHECKMS-LABEL: define %class.C* @"\01??0C@@QEAA@PEAHPEAD@Z"(%class.C* returned %this, i32* %i, i8* %c)
// CHECKMS-LABEL: define void @"\01??1C@@UEAA@XZ"(%class.C* %this)

class D : public virtual A {
public:
  D();
  ~D();
};

D::D() { }
D::~D() { }

// CHECKGEN-LABEL: define void @_ZN1DC1Ev(%class.D* %this)
// CHECKGEN-LABEL: define void @_ZN1DC2Ev(%class.D* %this, i8** %vtt)
// CHECKGEN-LABEL: define void @_ZN1DD1Ev(%class.D* %this)
// CHECKGEN-LABEL: define void @_ZN1DD2Ev(%class.D* %this, i8** %vtt)

// CHECKARM-LABEL: define %class.D* @_ZN1DC1Ev(%class.D* returned %this)
// CHECKARM-LABEL: define %class.D* @_ZN1DC2Ev(%class.D* returned %this, i8** %vtt)
// CHECKARM-LABEL: define %class.D* @_ZN1DD1Ev(%class.D* returned %this)
// CHECKARM-LABEL: define %class.D* @_ZN1DD2Ev(%class.D* returned %this, i8** %vtt)

// CHECKMS-LABEL: define %class.D* @"\01??0D@@QEAA@XZ"(%class.D* returned %this, i32 %is_most_derived)
// CHECKMS-LABEL: define void @"\01??1D@@QEAA@XZ"(%class.D* %this)

class E {
public:
  E();
  virtual ~E();
};

E* gete();

void test_destructor() {
  const E& e1 = E();
  E* e2 = gete();
  e2->~E();
}

// CHECKARM-LABEL: define void @_Z15test_destructorv()

// Verify that virtual calls to destructors are not marked with a 'returned'
// this parameter at the call site...
// CHECKARM: [[VFN:%.*]] = getelementptr inbounds %class.E* (%class.E*)**
// CHECKARM: [[THUNK:%.*]] = load %class.E* (%class.E*)** [[VFN]]
// CHECKARM: call %class.E* [[THUNK]](%class.E* %

// ...but static calls create declarations with 'returned' this
// CHECKARM: {{%.*}} = call %class.E* @_ZN1ED1Ev(%class.E* %

// CHECKARM: declare %class.E* @_ZN1ED1Ev(%class.E* returned)
<|endoftext|>


<commit_before>/*
 * Asynchronous local file access.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "istream_file.hxx"
#include "istream_buffer.hxx"
#include "buffered_io.hxx"
#include "system/fd_util.h"
#include "gerrno.h"
#include "pool.hxx"
#include "fb_pool.hxx"
#include "SliceFifoBuffer.hxx"
#include "event/Event.hxx"
#include "event/Callback.hxx"
#include "util/Cast.hxx"

#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <limits.h>

/**
 * If EAGAIN occurs (on NFS), we try again after 100ms.  We can't
 * check EV_READ, because the kernel always indicates VFS files as
 * "readable without blocking".
 */
static const struct timeval file_retry_timeout = {
    .tv_sec = 0,
    .tv_usec = 100000,
};

struct FileIstream {
    struct istream stream;
    int fd;

    FdType fd_type;

    /**
     * A timer to retry reading after EAGAIN.
     */
    Event event;

    off_t rest;
    SliceFifoBuffer buffer;
    const char *path;

    void CloseHandle() {
        if (fd < 0)
            return;

        event.Delete();

        close(fd);
        fd = -1;

        buffer.FreeIfDefined(fb_pool_get());
    }

    void Abort(GError *error) {
        CloseHandle();

        istream_deinit_abort(&stream, error);
    }

    /**
     * @return the number of bytes still in the buffer
     */
    size_t SubmitBuffer() {
        return istream_buffer_consume(&stream, buffer);
    }

    void EofDetected() {
        assert(fd >= 0);

        CloseHandle();
        istream_deinit_eof(&stream);
    }

    gcc_pure
    size_t GetMaxRead() const {
        if (rest != (off_t)-1 && rest < (off_t)INT_MAX)
            return (size_t)rest;
        else
            return INT_MAX;
    }

    void TryData();
    void TryDirect();

    void TryRead() {
        if (istream_check_direct(&stream, fd_type))
            TryDirect();
        else
            TryData();
    }

    void EventCallback() {
        TryRead();
    }
};

inline void
FileIstream::TryData()
{
    size_t buffer_rest = 0;

    if (buffer.IsNull()) {
        if (rest != 0)
            buffer.Allocate(fb_pool_get());
    } else {
        const size_t available = buffer.GetAvailable();
        if (available > 0) {
            buffer_rest = SubmitBuffer();
            if (buffer_rest == available)
                /* not a single byte was consumed: we may have been
                   closed, and we must bail out now */
                return;
        }
    }

    if (rest == 0) {
        if (buffer_rest == 0)
            EofDetected();
        return;
    }

    ssize_t nbytes = read_to_buffer(fd, buffer, GetMaxRead());
    if (nbytes == 0) {
        if (rest == (off_t)-1) {
            rest = 0;
            if (buffer_rest == 0)
                EofDetected();
        } else {
            GError *error =
                g_error_new(g_file_error_quark(), 0,
                            "premature end of file in '%s'", path);
            Abort(error);
        }
        return;
    } else if (nbytes == -1) {
        GError *error =
            g_error_new(errno_quark(), errno,
                        "failed to read from '%s': %s",
                        path, strerror(errno));
        Abort(error);
        return;
    } else if (nbytes > 0 && rest != (off_t)-1) {
        rest -= (off_t)nbytes;
        assert(rest >= 0);
    }

    assert(!buffer.IsEmpty());

    buffer_rest = SubmitBuffer();
    if (buffer_rest == 0 && rest == 0)
        EofDetected();
}

inline void
FileIstream::TryDirect()
{
    assert(stream.handler->direct != nullptr);

    /* first consume the rest of the buffer */
    if (SubmitBuffer() > 0)
        return;

    if (rest == 0) {
        EofDetected();
        return;
    }

    ssize_t nbytes = istream_invoke_direct(&stream,
                                           fd_type, fd,
                                           GetMaxRead());
    if (nbytes == ISTREAM_RESULT_CLOSED)
        /* this stream was closed during the direct() callback */
        return;

    if (nbytes > 0 || nbytes == ISTREAM_RESULT_BLOCKING) {
        /* -2 means the callback wasn't able to consume any data right
           now */
        if (nbytes > 0 && rest != (off_t)-1) {
            rest -= (off_t)nbytes;
            assert(rest >= 0);
            if (rest == 0)
                EofDetected();
        }
    } else if (nbytes == ISTREAM_RESULT_EOF) {
        if (rest == (off_t)-1) {
            EofDetected();
        } else {
            GError *error =
                g_error_new(g_file_error_quark(), 0,
                            "premature end of file in '%s'", path);
            Abort(error);
        }
    } else if (errno == EAGAIN) {
        /* this should only happen for splice(SPLICE_F_NONBLOCK) from
           NFS files - unfortunately we cannot use EV_READ here, so we
           just install a timer which retries after 100ms */

        event.Add(file_retry_timeout);
    } else {
        /* XXX */
        GError *error =
            g_error_new(errno_quark(), errno,
                        "failed to read from '%s': %s",
                        path, strerror(errno));
        Abort(error);
    }
}

/*
 * istream implementation
 *
 */

static inline FileIstream *
istream_to_file(struct istream *istream)
{
    return &ContainerCast2(*istream, &FileIstream::stream);
}

static off_t
istream_file_available(struct istream *istream, bool partial)
{
    FileIstream *file = istream_to_file(istream);
    off_t available = 0;

    if (file->rest != (off_t)-1)
        available = file->rest;
    else if (!partial)
        return (off_t)-1;
    else
        available = 0;

    available += file->buffer.GetAvailable();
    return available;
}

static off_t
istream_file_skip(struct istream *istream, off_t length)
{
    FileIstream *file = istream_to_file(istream);

    file->event.Delete();

    if (file->rest == (off_t)-1)
        return (off_t)-1;

    if (length == 0)
        return 0;

    /* clear the buffer; later we could optimize this function by
       flushing only the skipped number of bytes */
    file->buffer.Clear();

    if (length >= file->rest) {
        /* skip beyond EOF */

        length = file->rest;
        file->rest = 0;
    } else {
        /* seek the file descriptor */

        off_t ret = lseek(file->fd, length, SEEK_CUR);
        if (ret < 0)
            return -1;
        file->rest -= length;
    }

    return length;
}

static void
istream_file_read(struct istream *istream)
{
    FileIstream *file = istream_to_file(istream);

    assert(file->stream.handler != nullptr);

    file->event.Delete();

    file->TryRead();
}

static int
istream_file_as_fd(struct istream *istream)
{
    FileIstream *file = istream_to_file(istream);
    int fd = file->fd;

    file->event.Delete();
    istream_deinit(&file->stream);

    return fd;
}

static void
istream_file_close(struct istream *istream)
{
    FileIstream *file = istream_to_file(istream);

    file->CloseHandle();

    istream_deinit(&file->stream);
}

static const struct istream_class istream_file = {
    .available = istream_file_available,
    .skip = istream_file_skip,
    .read = istream_file_read,
    .as_fd = istream_file_as_fd,
    .close = istream_file_close,
};

/*
 * constructor and public methods
 *
 */

struct istream *
istream_file_fd_new(struct pool *pool, const char *path,
                    int fd, FdType fd_type, off_t length)
{
    assert(fd >= 0);
    assert(length >= -1);

    auto file = NewFromPool<FileIstream>(*pool);
    istream_init(&file->stream, &istream_file, pool);
    file->fd = fd;
    file->fd_type = fd_type;
    file->rest = length;
    file->buffer.SetNull();
    file->path = path;

    file->event.SetTimer(MakeSimpleEventCallback(FileIstream, EventCallback),
                         file);

    return &file->stream;
}

struct istream *
istream_file_stat_new(struct pool *pool, const char *path, struct stat *st,
                      GError **error_r)
{
    assert(path != nullptr);
    assert(st != nullptr);

    int fd = open_cloexec(path, O_RDONLY|O_NOCTTY, 0);
    if (fd < 0) {
        set_error_errno(error_r);
        g_prefix_error(error_r, "Failed to open %s: ", path);
        return nullptr;
    }

    if (fstat(fd, st) < 0) {
        set_error_errno(error_r);
        g_prefix_error(error_r, "Failed to stat %s: ", path);
        close(fd);
        return nullptr;
    }

    FdType fd_type = FdType::FD_FILE;
    off_t size = st->st_size;

    if (S_ISCHR(st->st_mode)) {
        fd_type = FdType::FD_CHARDEV;
        size = -1;
    }

    return istream_file_fd_new(pool, path, fd, fd_type, size);
}

struct istream *
istream_file_new(struct pool *pool, const char *path, off_t length,
                 GError **error_r)
{
    assert(length >= -1);

    int fd = open_cloexec(path, O_RDONLY|O_NOCTTY, 0);
    if (fd < 0) {
        set_error_errno(error_r);
        g_prefix_error(error_r, "Failed to open %s: ", path);
        return nullptr;
    }

    return istream_file_fd_new(pool, path, fd, FdType::FD_FILE, length);
}

int
istream_file_fd(struct istream *istream)
{
    assert(istream != nullptr);
    assert(istream->cls == &istream_file);

    FileIstream *file = istream_to_file(istream);

    assert(file->fd >= 0);

    return file->fd;
}

bool
istream_file_set_range(struct istream *istream, off_t start, off_t end)
{
    assert(istream != nullptr);
    assert(istream->cls == &istream_file);
    assert(start >= 0);
    assert(end >= start);

    FileIstream *file = istream_to_file(istream);
    assert(file->fd >= 0);
    assert(file->rest >= 0);
    assert(file->buffer.IsNull());
    assert(end <= file->rest);

    if (start > 0 && lseek(file->fd, start, SEEK_CUR) < 0)
        return false;

    file->rest = end - start;
    return true;
}
<commit_msg>istream_file: use class Istream<commit_after>/*
 * Asynchronous local file access.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "istream_file.hxx"
#include "istream_buffer.hxx"
#include "istream_oo.hxx"
#include "buffered_io.hxx"
#include "system/fd_util.h"
#include "gerrno.h"
#include "pool.hxx"
#include "fb_pool.hxx"
#include "SliceFifoBuffer.hxx"
#include "event/Event.hxx"
#include "event/Callback.hxx"

#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <limits.h>

/**
 * If EAGAIN occurs (on NFS), we try again after 100ms.  We can't
 * check EV_READ, because the kernel always indicates VFS files as
 * "readable without blocking".
 */
static const struct timeval file_retry_timeout = {
    .tv_sec = 0,
    .tv_usec = 100000,
};

struct FileIstream final : public Istream {
    int fd;

    FdType fd_type;

    /**
     * A timer to retry reading after EAGAIN.
     */
    Event event;

    off_t rest;
    SliceFifoBuffer buffer;
    const char *path;

    FileIstream(struct pool &p, int _fd, FdType _fd_type, off_t _length,
                const char *_path)
        :Istream(p),
         fd(_fd), fd_type(_fd_type), rest(_length),
         path(_path) {
        event.SetTimer(MakeSimpleEventCallback(FileIstream, EventCallback),
                       this);
    }

    static FileIstream &Cast2(struct istream &i) {
        return (FileIstream &)Istream::Cast(i);
    }

    void CloseHandle() {
        if (fd < 0)
            return;

        event.Delete();

        close(fd);
        fd = -1;

        buffer.FreeIfDefined(fb_pool_get());
    }

    void Abort(GError *error) {
        CloseHandle();
        DestroyError(error);
    }

    /**
     * @return the number of bytes still in the buffer
     */
    size_t SubmitBuffer() {
        return ConsumeFromBuffer(buffer);
    }

    void EofDetected() {
        assert(fd >= 0);

        CloseHandle();
        DestroyEof();
    }

    gcc_pure
    size_t GetMaxRead() const {
        if (rest != (off_t)-1 && rest < (off_t)INT_MAX)
            return (size_t)rest;
        else
            return INT_MAX;
    }

    void TryData();
    void TryDirect();

    void TryRead() {
        if (CheckDirect(fd_type))
            TryDirect();
        else
            TryData();
    }

    void EventCallback() {
        TryRead();
    }

    /* virtual methods from class Istream */

    off_t GetAvailable(bool partial) override;
    off_t Skip(gcc_unused off_t length) override;

    void Read() override {
        event.Delete();
        TryRead();
    }

    int AsFd() override;
    void Close() override {
        CloseHandle();
        Destroy();
    }
};

inline void
FileIstream::TryData()
{
    size_t buffer_rest = 0;

    if (buffer.IsNull()) {
        if (rest != 0)
            buffer.Allocate(fb_pool_get());
    } else {
        const size_t available = buffer.GetAvailable();
        if (available > 0) {
            buffer_rest = SubmitBuffer();
            if (buffer_rest == available)
                /* not a single byte was consumed: we may have been
                   closed, and we must bail out now */
                return;
        }
    }

    if (rest == 0) {
        if (buffer_rest == 0)
            EofDetected();
        return;
    }

    ssize_t nbytes = read_to_buffer(fd, buffer, GetMaxRead());
    if (nbytes == 0) {
        if (rest == (off_t)-1) {
            rest = 0;
            if (buffer_rest == 0)
                EofDetected();
        } else {
            GError *error =
                g_error_new(g_file_error_quark(), 0,
                            "premature end of file in '%s'", path);
            Abort(error);
        }
        return;
    } else if (nbytes == -1) {
        GError *error =
            g_error_new(errno_quark(), errno,
                        "failed to read from '%s': %s",
                        path, strerror(errno));
        Abort(error);
        return;
    } else if (nbytes > 0 && rest != (off_t)-1) {
        rest -= (off_t)nbytes;
        assert(rest >= 0);
    }

    assert(!buffer.IsEmpty());

    buffer_rest = SubmitBuffer();
    if (buffer_rest == 0 && rest == 0)
        EofDetected();
}

inline void
FileIstream::TryDirect()
{
    /* first consume the rest of the buffer */
    if (SubmitBuffer() > 0)
        return;

    if (rest == 0) {
        EofDetected();
        return;
    }

    ssize_t nbytes = InvokeDirect(fd_type, fd, GetMaxRead());
    if (nbytes == ISTREAM_RESULT_CLOSED)
        /* this stream was closed during the direct() callback */
        return;

    if (nbytes > 0 || nbytes == ISTREAM_RESULT_BLOCKING) {
        /* -2 means the callback wasn't able to consume any data right
           now */
        if (nbytes > 0 && rest != (off_t)-1) {
            rest -= (off_t)nbytes;
            assert(rest >= 0);
            if (rest == 0)
                EofDetected();
        }
    } else if (nbytes == ISTREAM_RESULT_EOF) {
        if (rest == (off_t)-1) {
            EofDetected();
        } else {
            GError *error =
                g_error_new(g_file_error_quark(), 0,
                            "premature end of file in '%s'", path);
            Abort(error);
        }
    } else if (errno == EAGAIN) {
        /* this should only happen for splice(SPLICE_F_NONBLOCK) from
           NFS files - unfortunately we cannot use EV_READ here, so we
           just install a timer which retries after 100ms */

        event.Add(file_retry_timeout);
    } else {
        /* XXX */
        GError *error =
            g_error_new(errno_quark(), errno,
                        "failed to read from '%s': %s",
                        path, strerror(errno));
        Abort(error);
    }
}

/*
 * istream implementation
 *
 */

off_t
FileIstream::GetAvailable(bool partial)
{
    off_t available;
    if (rest != (off_t)-1)
        available = rest;
    else if (!partial)
        return (off_t)-1;
    else
        available = 0;

    available += buffer.GetAvailable();
    return available;
}

off_t
FileIstream::Skip(off_t length)
{
    event.Delete();

    if (rest == (off_t)-1)
        return (off_t)-1;

    if (length == 0)
        return 0;

    /* clear the buffer; later we could optimize this function by
       flushing only the skipped number of bytes */
    buffer.Clear();

    if (length >= rest) {
        /* skip beyond EOF */

        length = rest;
        rest = 0;
    } else {
        /* seek the file descriptor */

        off_t ret = lseek(fd, length, SEEK_CUR);
        if (ret < 0)
            return -1;
        rest -= length;
    }

    return length;
}

int
FileIstream::AsFd()
{
    int result_fd = fd;

    event.Delete();
    Destroy();

    return result_fd;
}

/*
 * constructor and public methods
 *
 */

struct istream *
istream_file_fd_new(struct pool *pool, const char *path,
                    int fd, FdType fd_type, off_t length)
{
    assert(fd >= 0);
    assert(length >= -1);

    return NewIstream<FileIstream>(*pool, fd, fd_type, length, path);
}

struct istream *
istream_file_stat_new(struct pool *pool, const char *path, struct stat *st,
                      GError **error_r)
{
    assert(path != nullptr);
    assert(st != nullptr);

    int fd = open_cloexec(path, O_RDONLY|O_NOCTTY, 0);
    if (fd < 0) {
        set_error_errno(error_r);
        g_prefix_error(error_r, "Failed to open %s: ", path);
        return nullptr;
    }

    if (fstat(fd, st) < 0) {
        set_error_errno(error_r);
        g_prefix_error(error_r, "Failed to stat %s: ", path);
        close(fd);
        return nullptr;
    }

    FdType fd_type = FdType::FD_FILE;
    off_t size = st->st_size;

    if (S_ISCHR(st->st_mode)) {
        fd_type = FdType::FD_CHARDEV;
        size = -1;
    }

    return istream_file_fd_new(pool, path, fd, fd_type, size);
}

struct istream *
istream_file_new(struct pool *pool, const char *path, off_t length,
                 GError **error_r)
{
    assert(length >= -1);

    int fd = open_cloexec(path, O_RDONLY|O_NOCTTY, 0);
    if (fd < 0) {
        set_error_errno(error_r);
        g_prefix_error(error_r, "Failed to open %s: ", path);
        return nullptr;
    }

    return istream_file_fd_new(pool, path, fd, FdType::FD_FILE, length);
}

int
istream_file_fd(struct istream *istream)
{
    assert(istream != nullptr);

    auto &file = FileIstream::Cast2(*istream);
    assert(file.fd >= 0);
    return file.fd;
}

bool
istream_file_set_range(struct istream *istream, off_t start, off_t end)
{
    assert(istream != nullptr);
    assert(start >= 0);
    assert(end >= start);

    auto &file = FileIstream::Cast2(*istream);
    assert(file.fd >= 0);
    assert(file.rest >= 0);
    assert(file.buffer.IsNull());
    assert(end <= file.rest);

    if (start > 0 && lseek(file.fd, start, SEEK_CUR) < 0)
        return false;

    file.rest = end - start;
    return true;
}
<|endoftext|>


<commit_before>#ifndef IMPLSTORE_HPP
# define IMPLSTORE_HPP
# pragma once

#include <cassert>

#include <cstddef>

#include <type_traits>

#include <utility>

namespace generic
{

template <class U, ::std::size_t N = 64>
class implstore
{
public:
  static constexpr ::std::size_t const buffer_size = N;

  using value_type = U;

  template <typename ...A, typename =
    typename ::std::enable_if<::std::is_constructible<U, A...>{}>::type
  >
  implstore(A&& ...args)
  {
    static_assert(sizeof(U) <= sizeof(store_), "impl too large");
    new (static_cast<void*>(&store_)) U(::std::forward<A>(args)...);
  }

  ~implstore() { get()->~U(); }

  implstore(implstore const& other) : implstore(other, nullptr) { }

  implstore(implstore&& other) : implstore(::std::move(other), nullptr) { }

  template <::std::size_t M, typename K = U>
  implstore(implstore<U, M> const& other,
    typename ::std::enable_if<
      ::std::is_copy_constructible<K>{}
    >::type* = nullptr)
  {
    new (static_cast<void*>(&store_)) U(*other);
  }

  template <::std::size_t M, typename K = U>
  implstore(implstore<U, M>&& other,
    typename ::std::enable_if<
      ::std::is_move_constructible<K>{}
    >::type* = nullptr)
  {
    new (static_cast<void*>(&store_)) U(::std::move(*other));
  }

  implstore& operator=(implstore const& rhs)
  {
    return operator=<N, U>(rhs);
  }

  template <::std::size_t M, typename K = U, typename =
    typename ::std::enable_if<
      ::std::is_copy_assignable<K>{}
    >::type
  >
  implstore& operator=(implstore<U, M> const& rhs)
  {
    assert(this != &rhs);
    **this = *rhs;

    return *this;
  }

  implstore& operator=(implstore&& rhs)
  {
    return operator=<N, U>(::std::move(rhs));
  }

  template <::std::size_t M, typename K = U, typename =
    typename ::std::enable_if<
      ::std::is_move_assignable<K>{}
    >::type
  >
  implstore& operator=(implstore<U, M>&& rhs)
  {
    assert(this != &rhs);
    **this = ::std::move(*rhs);

    return *this;
  }

  U const* operator->() const noexcept
  {
    return reinterpret_cast<U const*>(&store_);
  }

  U* operator->() noexcept
  {
    return reinterpret_cast<U*>(&store_);
  }

  U const& operator*() const noexcept
  {
    return *reinterpret_cast<U const*>(&store_);
  }

  U& operator*() noexcept
  {
    return *reinterpret_cast<U*>(&store_);
  }

  U const* get() const noexcept
  {
    return reinterpret_cast<U const*>(&store_);
  }

  U* get() noexcept
  {
    return reinterpret_cast<U*>(&store_);
  }

private:
  typename ::std::aligned_storage<N>::type store_;
};

}

#endif // IMPLSTORE_HPP
<commit_msg>some fixes<commit_after>#ifndef IMPLSTORE_HPP
# define IMPLSTORE_HPP
# pragma once

#include <cassert>

#include <cstddef>

#include <type_traits>

#include <utility>

namespace generic
{

template <class U, ::std::size_t N = 64>
class implstore
{
public:
  static constexpr ::std::size_t const buffer_size = N;

  using value_type = U;

  template <typename ...A, typename =
    typename ::std::enable_if<::std::is_constructible<U, A...>{}>::type
  >
  implstore(A&& ...args)
  {
    static_assert(sizeof(store_) >= sizeof(U), "impl too large");
    new (static_cast<void*>(&store_)) U(::std::forward<A>(args)...);
  }

  ~implstore() { get()->~U(); }

  implstore(implstore const& other) : implstore(other, nullptr) { }

  implstore(implstore&& other) : implstore(::std::move(other), nullptr) { }

  template <::std::size_t M, typename K = U>
  implstore(implstore<U, M> const& other,
    typename ::std::enable_if<
      ::std::is_copy_constructible<K>{}
    >::type* = nullptr)
  {
    new (static_cast<void*>(&store_)) U(*other);
  }

  template <::std::size_t M, typename K = U>
  implstore(implstore<U, M>&& other,
    typename ::std::enable_if<
      ::std::is_move_constructible<K>{}
    >::type* = nullptr)
  {
    new (static_cast<void*>(&store_)) U(::std::move(*other));
  }

  implstore& operator=(implstore const& rhs)
  {
    return operator=<N, U>(rhs);
  }

  template <::std::size_t M, typename K = U, typename =
    typename ::std::enable_if<
      ::std::is_copy_assignable<K>{}
    >::type
  >
  implstore& operator=(implstore<U, M> const& rhs)
  {
    assert(this != &rhs);
    **this = *rhs;

    return *this;
  }

  implstore& operator=(implstore&& rhs)
  {
    return operator=<N, U>(::std::move(rhs));
  }

  template <::std::size_t M, typename K = U, typename =
    typename ::std::enable_if<
      ::std::is_move_assignable<K>{}
    >::type
  >
  implstore& operator=(implstore<U, M>&& rhs)
  {
    assert(this != &rhs);
    **this = ::std::move(*rhs);

    return *this;
  }

  U const* operator->() const noexcept
  {
    return reinterpret_cast<U const*>(&store_);
  }

  U* operator->() noexcept
  {
    return reinterpret_cast<U*>(&store_);
  }

  U const& operator*() const noexcept
  {
    return *reinterpret_cast<U const*>(&store_);
  }

  U& operator*() noexcept
  {
    return *reinterpret_cast<U*>(&store_);
  }

  U const* get() const noexcept
  {
    return reinterpret_cast<U const*>(&store_);
  }

  U* get() noexcept
  {
    return reinterpret_cast<U*>(&store_);
  }

private:
  typename ::std::aligned_storage<N>::type store_;
};

}

#endif // IMPLSTORE_HPP
<|endoftext|>


<commit_before>#ifndef IMPLSTORE_HPP
# define IMPLSTORE_HPP
# pragma once

#include <cassert>

#include <cstddef>

#include <type_traits>

#include <utility>

namespace generic
{

template <class U, ::std::size_t N = 64>
class implstore
{
  typename ::std::aligned_storage_t<N> store_;

public:
  static constexpr ::std::size_t const buffer_size = N;

  using value_type = U;

  template <typename ...A, typename =
    typename ::std::enable_if<::std::is_constructible<U, A...>{}>::type
  >
  explicit implstore(A&& ...args)
  {
    static_assert(sizeof(store_) >= sizeof(U), "impl too large");
    ::new (static_cast<void*>(&store_)) U(::std::forward<A>(args)...);
  }

  ~implstore() { get()->~U(); }

  implstore(implstore const& other) : implstore(other, nullptr) { }

  implstore(implstore&& other) : implstore(::std::move(other), nullptr) { }

  template <::std::size_t M, typename K = U>
  implstore(implstore<U, M> const& other,
    typename ::std::enable_if<
      ::std::is_copy_constructible<K>{}
    >::type* = nullptr)
  {
    ::new (static_cast<void*>(&store_)) U(*other);
  }

  template <::std::size_t M, typename K = U>
  implstore(implstore<U, M>&& other,
    typename ::std::enable_if<
      ::std::is_move_constructible<K>{}
    >::type* = nullptr)
  {
    ::new (static_cast<void*>(&store_)) U(::std::move(*other));
  }

  implstore& operator=(implstore const& rhs)
  {
    return operator=<N, U>(rhs);
  }

  template <::std::size_t M, typename K = U, typename =
    typename ::std::enable_if<
      ::std::is_copy_assignable<K>{}
    >::type
  >
  implstore& operator=(implstore<U, M> const& rhs)
  {
    assert(this != &rhs);
    **this = *rhs;

    return *this;
  }

  implstore& operator=(implstore&& rhs)
  {
    return operator=<N, U>(::std::move(rhs));
  }

  template <::std::size_t M, typename K = U, typename =
    typename ::std::enable_if<
      ::std::is_move_assignable<K>{}
    >::type
  >
  implstore& operator=(implstore<U, M>&& rhs)
  {
    assert(this != &rhs);
    **this = ::std::move(*rhs);

    return *this;
  }

  U const* operator->() const noexcept
  {
    return reinterpret_cast<U const*>(&store_);
  }

  U* operator->() noexcept
  {
    return reinterpret_cast<U*>(&store_);
  }

  U const& operator*() const noexcept
  {
    return *reinterpret_cast<U const*>(&store_);
  }

  U& operator*() noexcept
  {
    return *reinterpret_cast<U*>(&store_);
  }

  U const* get() const noexcept
  {
    return reinterpret_cast<U const*>(&store_);
  }

  U* get() noexcept
  {
    return reinterpret_cast<U*>(&store_);
  }
};

}

#endif // IMPLSTORE_HPP
<commit_msg>some fixes<commit_after>#ifndef IMPLSTORE_HPP
# define IMPLSTORE_HPP
# pragma once

#include <cassert>

#include <cstddef>

#include <type_traits>

#include <utility>

namespace generic
{

template <class U, ::std::size_t N = 64>
class implstore
{
  typename ::std::aligned_storage_t<N> store_;

public:
  static constexpr ::std::size_t const buffer_size = N;

  using value_type = U;

  template <typename ...A>
  explicit implstore(A&& ...args)
  {
    static_assert(::std::is_constructible<U, A...>{}, "cannot construct U");
    static_assert(sizeof(store_) >= sizeof(U), "impl too large");
    ::new (static_cast<void*>(&store_)) U(::std::forward<A>(args)...);
  }

  ~implstore() { get()->~U(); }

  implstore(implstore const& other) : implstore(other, nullptr) { }

  implstore(implstore&& other) : implstore(::std::move(other), nullptr) { }

  template <::std::size_t M, typename K = U>
  implstore(implstore<U, M> const& other,
    ::std::enable_if_t<::std::is_copy_constructible<K>{}>* = {})
  {
    ::new (static_cast<void*>(&store_)) U(*other);
  }

  template <::std::size_t M, typename K = U>
  implstore(implstore<U, M>&& other,
    ::std::enable_if_t<::std::is_move_constructible<K>{}>* = nullptr)
  {
    ::new (static_cast<void*>(&store_)) U(::std::move(*other));
  }

  implstore& operator=(implstore const& rhs)
  {
    return operator=<N, U>(rhs);
  }

  template <::std::size_t M, typename K = U, typename =
    typename ::std::enable_if<
      ::std::is_copy_assignable<K>{}
    >::type
  >
  implstore& operator=(implstore<U, M> const& rhs)
  {
    assert(this != &rhs);
    **this = *rhs;

    return *this;
  }

  implstore& operator=(implstore&& rhs)
  {
    return operator=<N, U>(::std::move(rhs));
  }

  template <::std::size_t M, typename K = U, typename =
    typename ::std::enable_if<
      ::std::is_move_assignable<K>{}
    >::type
  >
  implstore& operator=(implstore<U, M>&& rhs)
  {
    assert(this != &rhs);
    **this = ::std::move(*rhs);

    return *this;
  }

  U const* operator->() const noexcept
  {
    return reinterpret_cast<U const*>(&store_);
  }

  U* operator->() noexcept
  {
    return reinterpret_cast<U*>(&store_);
  }

  U const& operator*() const noexcept
  {
    return *reinterpret_cast<U const*>(&store_);
  }

  U& operator*() noexcept
  {
    return *reinterpret_cast<U*>(&store_);
  }

  U const* get() const noexcept
  {
    return reinterpret_cast<U const*>(&store_);
  }

  U* get() noexcept
  {
    return reinterpret_cast<U*>(&store_);
  }
};

}

#endif // IMPLSTORE_HPP
<|endoftext|>


<commit_before>// Copyright 2010-2012 RethinkDB, all rights reserved.
#ifndef ARCH_IO_TIMER_PROVIDER_HPP_
#define ARCH_IO_TIMER_PROVIDER_HPP_

// We pick the right timer provider (that impelements OS level timer
// interface) depending on which system we're on. Some older kernels
// don't support fdtimers, so we have to resort to signals.

#ifdef LEGACY_LINUX
#define RDB_USE_TIMER_SIGNAL_PROVIDER 1
#else
#if __MACH__
#define RDB_USE_TIMER_ITIMER_PROVIDER 1
#else
#define RDB_USE_TIMERFD_PROVIDER 1
#endif  // __MACH__
#endif  // LEGACY_LINUX

#if RDB_USE_TIMER_SIGNAL_PROVIDER

#include "arch/io/timer/timer_signal_provider.hpp"
typedef timer_signal_provider_t timer_provider_t;

#elif RDB_USE_TIMER_ITIMER_PROVIDER

// TODO(OSX) Any other ways to do timing?  Just get rid of timerfd?
// TODO(OSX) timerfd is linux-specific, so timer_signal_provider_t should be in the else block.
#include "arch/io/timer/timer_itimer_provider.hpp"
typedef timer_itimer_provider_t timer_provider_t;

#elif RDB_USE_TIMERFD_PROVIDER

#include "arch/io/timer/timerfd_provider.hpp"
typedef timerfd_provider_t timer_provider_t;

#else

#error "No timer provider define specified."

#endif  // RDB_USE_TIMER_..._PROVIDER

/* Timer provider callback */
struct timer_provider_callback_t {
    virtual ~timer_provider_callback_t() {}
    virtual void on_timer(int nexpirations) = 0;
};

#endif // ARCH_IO_TIMER_PROVIDER_HPP_
<commit_msg>Removed some old TODO comments in timer_provider.hpp.<commit_after>// Copyright 2010-2012 RethinkDB, all rights reserved.
#ifndef ARCH_IO_TIMER_PROVIDER_HPP_
#define ARCH_IO_TIMER_PROVIDER_HPP_

// We pick the right timer provider (that impelements OS level timer
// interface) depending on which system we're on. Some older kernels
// don't support fdtimers, so we have to resort to signals.

#ifdef LEGACY_LINUX
#define RDB_USE_TIMER_SIGNAL_PROVIDER 1
#else
#if __MACH__
#define RDB_USE_TIMER_ITIMER_PROVIDER 1
#else
#define RDB_USE_TIMERFD_PROVIDER 1
#endif  // __MACH__
#endif  // LEGACY_LINUX

#if RDB_USE_TIMER_SIGNAL_PROVIDER

#include "arch/io/timer/timer_signal_provider.hpp"
typedef timer_signal_provider_t timer_provider_t;

#elif RDB_USE_TIMER_ITIMER_PROVIDER

#include "arch/io/timer/timer_itimer_provider.hpp"
typedef timer_itimer_provider_t timer_provider_t;

#elif RDB_USE_TIMERFD_PROVIDER

#include "arch/io/timer/timerfd_provider.hpp"
typedef timerfd_provider_t timer_provider_t;

#else

#error "No timer provider define specified."

#endif  // RDB_USE_TIMER_..._PROVIDER

/* Timer provider callback */
struct timer_provider_callback_t {
    virtual ~timer_provider_callback_t() {}
    virtual void on_timer(int nexpirations) = 0;
};

#endif // ARCH_IO_TIMER_PROVIDER_HPP_
<|endoftext|>


<commit_before>#pragma once
#include <vector>
#include <boost/variant.hpp>
#include <boost/iterator/indirect_iterator.hpp>
#include <memory>
#include <iostream>
#include "utility/initializers.hpp"

namespace backend {

namespace ctype {

class monotype_t;
class polytype_t;
class sequence_t;
class tuple_t;
class fn_t;
class cuarray_t;

namespace detail {
typedef boost::variant<
    monotype_t &,
    polytype_t &,
    sequence_t &,
    tuple_t &,
    fn_t &,
    cuarray_t &
    > type_base;

struct make_type_base_visitor
    : boost::static_visitor<type_base>
{
    make_type_base_visitor(void *p);

    template<typename Derived>
    type_base operator()(const Derived &) const {
        // use of std::ref disambiguates variant's copy constructor dispatch
        return type_base(std::ref(*reinterpret_cast<Derived*>(ptr)));
    }
    void *ptr;
};

type_base make_type_base(void *ptr, const type_base &other);

}

/*!
  \addtogroup ctypes
  @{
 */

//! Parent type for all C++ implementation types
/*! Not intended to be instantiated directly

  \param self

  \return
*/
class type_t
    : public detail::type_base
{
protected:
    typedef detail::type_base super_t;
    //! Derived constructor
/*! To be called by derived object during construction.

  \param self Reference to derived object being constructed.
*/
    template<typename Derived>
    type_t(Derived &self)
        : super_t(std::ref(self)) //use of std::ref disambiguates variant's copy constructor dispatch
        {}
public:
    //! Copy constructor
    type_t(const type_t &other);

};

//! Monomorphic type
/*! Can be used standalone or as a parent class */
class monotype_t :
    public type_t
{
protected:
    const std::string m_name;
    std::vector<std::shared_ptr<type_t> > m_params;
public:
    //! Basic onstructor
/*! This constructor is used for simple monotypes with no subtypes.
  \param name The name of type.
*/
    monotype_t(const std::string &name);

    //! Derived constructor
/*! To be called during construction of derived object

  \param self Reference to derived object under construction.
  \param name Name of type.

*/
    template<typename Derived>
    monotype_t(Derived &self,
               const std::string &name)
        : type_t(self),
          m_name(name)
        {}

    //! Derived constructor
/*! This constructor is instantiated by subclasses during
 *  construction. It accepts a list of subtypes that define this
 *  type. For example, Seq(Seq(Int)) is a nested type, and
 *  constructing it requires defining subtypes.

  \param self Reference to derived object being constructed.
  \param name The name of the type.
  \param params List of subtypes.
*/
    template<typename Derived>
    monotype_t(Derived& self,
               const std::string &name,
               std::vector<std::shared_ptr<type_t> > &&params)
        : type_t(self), m_name(name), m_params(std::move(params)) {}

    //! Gets name of type.
    const std::string& name(void) const;
    //! Type of iterator to subtypes in this monotype
    typedef decltype(boost::make_indirect_iterator(m_params.cbegin())) const_iterator;
    //! Iterator to the beginning of the subtypes contained in this monotype
    const_iterator begin() const;
    //! Iterator to the end of the subtypes contained in this monotype
    const_iterator end() const;
    typedef decltype(m_params.cbegin()) const_ptr_iterator;
    //! Gets an iterator to the pointer of the first type held by this tuple
    const_ptr_iterator p_begin() const;
    //! Gets an iterator to the pointer of the last type held by this tuple
    const_ptr_iterator p_end() const;
    //! Number of subtypes in this monotype. Returns 0 for non-nested types.
    int size() const;
};

extern std::shared_ptr<monotype_t> int32_mt;
extern std::shared_ptr<monotype_t> int64_mt;
extern std::shared_ptr<monotype_t> uint32_mt;
extern std::shared_ptr<monotype_t> uint64_mt;
extern std::shared_ptr<monotype_t> float32_mt;
extern std::shared_ptr<monotype_t> float64_mt;
extern std::shared_ptr<monotype_t> bool_mt;
extern std::shared_ptr<monotype_t> void_mt;

//! Sequence type.
/* Can be used directly or as a parent class */
class sequence_t :
        public monotype_t
{
public:
    //! Basic constructor
/*!
  \param sub Type of element of the Sequence
*/
    sequence_t(const std::shared_ptr<type_t> &sub);
    //! Derived constructor
/*! To be called during the construction of a derived object

  \param self Reference to the derived object under construction
  \param name Name of the Sequence-like type.
  \param sub Type of the element of the Sequence-like type.

*/
    template<typename Derived>
    sequence_t(Derived &self,
                      const std::string& name,
                      const std::shared_ptr<type_t>& sub) :
        monotype_t(self,
                   name,
                   utility::make_vector<std::shared_ptr<type_t> >(sub)) {}
    //! Gets the type of the element of the Sequence
    const type_t& sub() const;
    //! Gets a pointer to the type of the element of the Sequence
    std::shared_ptr<type_t> p_sub() const;
};

//! Tuple type.
class tuple_t :
        public monotype_t
{
public:
    //! Constructor
/*!
  \param sub A vector of types contained in this tuple.
*/
    tuple_t(std::vector<std::shared_ptr<type_t> > && sub);
};

//! Function type
class fn_t :
        public monotype_t
{
public:
    //! Constructor
/*!
  \param args Tuple of argument types.
  \param result Result type.
*/
    fn_t(const std::shared_ptr<tuple_t> args,
         const std::shared_ptr<type_t> result);
    //! Gets the tuple of argument types.
    const tuple_t& args() const;
    //! Gets the result type.
    const type_t& result() const;
    //! Gets a pointer to the tuple of argument types.
    std::shared_ptr<tuple_t> p_args() const;
    //! Gets a pointer to the result type.
    std::shared_ptr<type_t> p_result() const;
};

//! Cuarray type
/*! This is a sequence type, but is actually a container in C++
   and has to print differently. */
class cuarray_t :
        public sequence_t {
public:
    cuarray_t(const std::shared_ptr<type_t> sub);
};

//! Polymorphic type
/* This is translated into C++ templated types.

*/
class polytype_t
    : public type_t {
private:
    std::vector<std::shared_ptr<type_t> > m_vars;
    std::shared_ptr<monotype_t> m_monotype;

public:
    //! Constructor
/*!

  \param vars Variables to instantiate type with.  Note that in
  contrast to the Copperhead \ref backend::polytype_t, the
  C++ \ref backend::ctype::polytype_t can be nested, which is why this
  is a \p vector of \ref backend::ctype::type_t rather than
  a \p vector of \ref backend::ctype::monotype_t.
  is a vector \param monotype Base type

  \return
*/
    polytype_t(std::vector<std::shared_ptr<type_t> >&& vars,
               std::shared_ptr<monotype_t> monotype);
//! Gets base type
    const monotype_t& monotype() const;
    //! Gets \p std::shared_ptr to base type
    std::shared_ptr<monotype_t> p_monotype() const;
    //! Type of iterator to type variables
    typedef decltype(boost::make_indirect_iterator(m_vars.cbegin())) const_iterator;
    //! Iterator to beginning of type variables
    const_iterator begin() const;
    //! Iterator to end of type variables
    const_iterator end() const;
    //! Type of iterator to std::shared_ptr objects which hold type variables
    typedef decltype(m_vars.cbegin()) const_ptr_iterator;
    //! Iterator to beginning of pointers of type variables
    const_ptr_iterator p_begin() const;
    //! Iterator to end of pointers of type variables
    const_ptr_iterator p_end() const;
};

/*
  @}
*/

}
}
<commit_msg>Documentation fix.<commit_after>#pragma once
#include <vector>
#include <boost/variant.hpp>
#include <boost/iterator/indirect_iterator.hpp>
#include <memory>
#include <iostream>
#include "utility/initializers.hpp"

namespace backend {

namespace ctype {

class monotype_t;
class polytype_t;
class sequence_t;
class tuple_t;
class fn_t;
class cuarray_t;

namespace detail {
typedef boost::variant<
    monotype_t &,
    polytype_t &,
    sequence_t &,
    tuple_t &,
    fn_t &,
    cuarray_t &
    > type_base;

struct make_type_base_visitor
    : boost::static_visitor<type_base>
{
    make_type_base_visitor(void *p);

    template<typename Derived>
    type_base operator()(const Derived &) const {
        // use of std::ref disambiguates variant's copy constructor dispatch
        return type_base(std::ref(*reinterpret_cast<Derived*>(ptr)));
    }
    void *ptr;
};

type_base make_type_base(void *ptr, const type_base &other);

}

/*!
  \addtogroup ctypes
  @{
 */

//! Parent type for all C++ implementation types
/*! Not intended to be instantiated directly

  \param self

  \return
*/
class type_t
    : public detail::type_base
{
protected:
    typedef detail::type_base super_t;
    //! Derived constructor
/*! To be called by derived object during construction.

  \param self Reference to derived object being constructed.
*/
    template<typename Derived>
    type_t(Derived &self)
        : super_t(std::ref(self)) //use of std::ref disambiguates variant's copy constructor dispatch
        {}
public:
    //! Copy constructor
    type_t(const type_t &other);

};

//! Monomorphic type
/*! Can be used standalone or as a parent class */
class monotype_t :
    public type_t
{
protected:
    const std::string m_name;
    std::vector<std::shared_ptr<type_t> > m_params;
public:
    //! Basic onstructor
/*! This constructor is used for simple monotypes with no subtypes.
  \param name The name of type.
*/
    monotype_t(const std::string &name);

    //! Derived constructor
/*! To be called during construction of derived object

  \param self Reference to derived object under construction.
  \param name Name of type.

*/
    template<typename Derived>
    monotype_t(Derived &self,
               const std::string &name)
        : type_t(self),
          m_name(name)
        {}

    //! Derived constructor
/*! This constructor is instantiated by subclasses during
 *  construction. It accepts a list of subtypes that define this
 *  type. For example, Seq(Seq(Int)) is a nested type, and
 *  constructing it requires defining subtypes.

  \param self Reference to derived object being constructed.
  \param name The name of the type.
  \param params List of subtypes.
*/
    template<typename Derived>
    monotype_t(Derived& self,
               const std::string &name,
               std::vector<std::shared_ptr<type_t> > &&params)
        : type_t(self), m_name(name), m_params(std::move(params)) {}

    //! Gets name of type.
    const std::string& name(void) const;
    //! Type of iterator to subtypes in this monotype
    typedef decltype(boost::make_indirect_iterator(m_params.cbegin())) const_iterator;
    //! Iterator to the beginning of the subtypes contained in this monotype
    const_iterator begin() const;
    //! Iterator to the end of the subtypes contained in this monotype
    const_iterator end() const;
    typedef decltype(m_params.cbegin()) const_ptr_iterator;
    //! Gets an iterator to the pointer of the first type held by this tuple
    const_ptr_iterator p_begin() const;
    //! Gets an iterator to the pointer of the last type held by this tuple
    const_ptr_iterator p_end() const;
    //! Number of subtypes in this monotype. Returns 0 for non-nested types.
    int size() const;
};

extern std::shared_ptr<monotype_t> int32_mt;
extern std::shared_ptr<monotype_t> int64_mt;
extern std::shared_ptr<monotype_t> uint32_mt;
extern std::shared_ptr<monotype_t> uint64_mt;
extern std::shared_ptr<monotype_t> float32_mt;
extern std::shared_ptr<monotype_t> float64_mt;
extern std::shared_ptr<monotype_t> bool_mt;
extern std::shared_ptr<monotype_t> void_mt;

//! Sequence type.
/* Can be used directly or as a parent class */
class sequence_t :
        public monotype_t
{
public:
    //! Basic constructor
/*!
  \param sub Type of element of the Sequence
*/
    sequence_t(const std::shared_ptr<type_t> &sub);
    //! Derived constructor
/*! To be called during the construction of a derived object

  \param self Reference to the derived object under construction
  \param name Name of the Sequence-like type.
  \param sub Type of the element of the Sequence-like type.

*/
    template<typename Derived>
    sequence_t(Derived &self,
                      const std::string& name,
                      const std::shared_ptr<type_t>& sub) :
        monotype_t(self,
                   name,
                   utility::make_vector<std::shared_ptr<type_t> >(sub)) {}
    //! Gets the type of the element of the Sequence
    const type_t& sub() const;
    //! Gets a pointer to the type of the element of the Sequence
    std::shared_ptr<type_t> p_sub() const;
};

//! Tuple type.
class tuple_t :
        public monotype_t
{
public:
    //! Constructor
/*!
  \param sub A vector of types contained in this tuple.
*/
    tuple_t(std::vector<std::shared_ptr<type_t> > && sub);
};

//! Function type
class fn_t :
        public monotype_t
{
public:
    //! Constructor
/*!
  \param args Tuple of argument types.
  \param result Result type.
*/
    fn_t(const std::shared_ptr<tuple_t> args,
         const std::shared_ptr<type_t> result);
    //! Gets the tuple of argument types.
    const tuple_t& args() const;
    //! Gets the result type.
    const type_t& result() const;
    //! Gets a pointer to the tuple of argument types.
    std::shared_ptr<tuple_t> p_args() const;
    //! Gets a pointer to the result type.
    std::shared_ptr<type_t> p_result() const;
};

//! Cuarray type
/*! This is a sequence type, but is actually a container in C++
   and has to print differently. */
class cuarray_t :
        public sequence_t {
public:
    cuarray_t(const std::shared_ptr<type_t> sub);
};

//! Polymorphic type
/*! This is translated into C++ templated types.  Note that since C++
   templated types can (and often are) nested, this type is somewhat
   different from its analogue Copperhead
   type \ref backend::polytype_t.  Specifically, instead of accepting
   a vector of monotypes, it accepts
   both \ref backend::ctype::monotype_t and backend::ctype::polytype_t
   objects as type variables.

   This reflects the less restrictive type system of C++ compared to
   Copperhead.  */
class polytype_t
    : public type_t {
private:
    std::vector<std::shared_ptr<type_t> > m_vars;
    std::shared_ptr<monotype_t> m_monotype;

public:
    //! Constructor
/*!

  \param vars Variables to instantiate type with.  Note that in
  contrast to the Copperhead \ref backend::polytype_t, the
  C++ \ref backend::ctype::polytype_t can be nested, which is why this
  is a \p vector of \ref backend::ctype::type_t rather than
  a \p vector of \ref backend::ctype::monotype_t.

  \param monotype Base type

  \return
*/
    polytype_t(std::vector<std::shared_ptr<type_t> >&& vars,
               std::shared_ptr<monotype_t> monotype);
//! Gets base type
    const monotype_t& monotype() const;
    //! Gets \p std::shared_ptr to base type
    std::shared_ptr<monotype_t> p_monotype() const;
    //! Type of iterator to type variables
    typedef decltype(boost::make_indirect_iterator(m_vars.cbegin())) const_iterator;
    //! Iterator to beginning of type variables
    const_iterator begin() const;
    //! Iterator to end of type variables
    const_iterator end() const;
    //! Type of iterator to std::shared_ptr objects which hold type variables
    typedef decltype(m_vars.cbegin()) const_ptr_iterator;
    //! Iterator to beginning of pointers of type variables
    const_ptr_iterator p_begin() const;
    //! Iterator to end of pointers of type variables
    const_ptr_iterator p_end() const;
};

/*
  @}
*/

}
}
<|endoftext|>


<commit_before>#ifndef HEAPS_H
#define HEAPS_H

#include"objects.hpp"
#include"generics.hpp"

#include<cstring>
#include<utility>
#include<set>

#include<boost/scoped_ptr.hpp>
#include<boost/noncopyable.hpp>

class Generic;
class ValueHolder;

void throw_DeallocError(void*);

/*-----------------------------------------------------------------------------
HeapTraverser
-----------------------------------------------------------------------------*/

class HeapTraverser {
public:
	virtual void traverse(Generic*) =0;
	virtual ~HeapTraverser(void) { }
};

/*
Utility class to wrap a GenericTraverser in a
HeapTraverser
*/
template<class T>
class ObjectsTraverser : public HeapTraverser {
private:
	T t;
public:
	template<class U>
	ObjectsTraverser(U u) : t(u) {
		GenericTraverser* ObjectsTraverser_template_should_derive_GenericTraverser
			= static_cast<GenericTraverser*>(&t);
	}
	template<class U, class V>
	ObjectsTraverser(U u, V v) : t(u, v) { }

	void traverse(Generic* gp) {
		gp->traverse_references(&t);
	}
};

/*-----------------------------------------------------------------------------
Semispaces
-----------------------------------------------------------------------------*/

class Semispace : boost::noncopyable {
private:
	void* mem;
	void* allocstart;
	void* allocpt;
	void* lifoallocstart;
	void* lifoallocpt;
	size_t prev_alloc;
	size_t max;
public:
	explicit Semispace(size_t);
	~Semispace();

	void* alloc(size_t);
	void dealloc(void*);

	void* lifo_alloc(size_t);
	void lifo_dealloc(Generic*);
	void lifo_dealloc_abort(void*);

	inline size_t size(void) const { return max; };
	inline size_t used(void) const {
		return (size_t)(((char*) allocpt) - ((char*) mem)) +
			(size_t)
			((((char*) mem) + max) - ((char*) lifoallocpt));
	};
	inline size_t free(void) const {
		return (size_t)(((char*) lifoallocpt) - ((char*) allocpt));
	}
	inline bool can_fit(size_t sz) const {
		return sz <= free();
	}

	void traverse_objects(HeapTraverser*) const;

	void clone(boost::scoped_ptr<Semispace>&, Generic*&) const;

	friend class Heap;
};

/*-----------------------------------------------------------------------------
ValueHolders
-----------------------------------------------------------------------------*/

class ValueHolder;

void throw_ValueHolderLinkingError(ValueHolder*);

class ValueHolder;
class LockedValueHolderRef;

/*smart pointer specifically for ValueHolder*/
class ValueHolderRef : boost::noncopyable {
private:
	ValueHolder* p;
public:
	ValueHolderRef(ValueHolder* np = 0) : p(np) { }
	/*don't define this here: just declare.
	The definition should occur after ValueHolder
	is completely declared.
	*/
	~ValueHolderRef();
	void reset(ValueHolder* np = 0);
	void swap(ValueHolderRef& n) {
		ValueHolder* tmp = p;
		p = n.p;
		n.p = tmp;
	}
	ValueHolder* operator->(void) { return p; }
	ValueHolder const* operator->(void) const { return p; }
	ValueHolder& operator*(void) { return *p; }
	ValueHolder const& operator*(void) const { return *p; }
	bool empty(void) const { return p == 0; }

	void insert(ValueHolderRef&);
	void remove(ValueHolderRef&);

	Object::ref value(void);

	friend class LockedValueHolderRef;
	friend class ValueHolder;
};

class ValueHolder {
private:
	boost::scoped_ptr<Semispace> sp;
	Object::ref val;

	/*
	Allow chaining, for example in
	the mailbox of a process.
	*/
	ValueHolderRef next;

	ValueHolder() { }
public:

	inline size_t used_total(void) const {
		size_t total = 0;
		for(ValueHolder const* pt = this; pt; pt = &*pt->next) {
			if(pt->sp) total += pt->sp->used();
		}
		return total;
	}

	void clone(ValueHolderRef&) const;

	void traverse_objects(HeapTraverser*) const;

	static void copy_object(ValueHolderRef&, Object::ref);

	friend class ValueHolderRef;
	friend class LockedValueHolderRef;
};

inline ValueHolderRef::~ValueHolderRef() {
	delete p;
}
inline void ValueHolderRef::reset(ValueHolder* np) {
	delete p;
	p = np;
}

inline void ValueHolderRef::insert(ValueHolderRef& o) {
	#ifdef DEBUG
		/*make sure there's exactly one element to insert*/
		if(!o.p) {
			throw_ValueHolderLinkingError(o.p);
		}
		if(o->next.p) {
			throw_ValueHolderLinkingError(o.p);
		}
	#endif
	o->next.p = p;
	p = o.p;
	o.p = 0; /*release*/
}

inline void ValueHolderRef::remove(ValueHolderRef& o) {
	#ifdef DEBUG
		if(o.p) {
			throw_ValueHolderLinkingError(o.p);
		}
	#endif
	if(!p) return;
	o.p = p;
	p = p->next.p;
	o->next.p = 0;
}

inline Object::ref ValueHolderRef::value(void) {
	if(p)	return p->val;
	else	return Object::nil();
}

/*-----------------------------------------------------------------------------
Heaps
-----------------------------------------------------------------------------*/

class Heap : boost::noncopyable {
private:
	boost::scoped_ptr<Semispace> main;
	bool tight;

	#ifndef ONLY_COPYING_GC
		enum GCType { gc_type_copying, gc_type_generational };
		GCType gc_type;

		/*Deferred GC type.*/
		GCType recommended_gc_type;
		/*
		We first decide to change GC types, but only
		assign to recommended_gc_type.  When the ssb
		is acquired, only then do we finalize the
		recommended type to the actual type.
		This is done in order to facilitate JIT.
		We can then JIT two versions of the code:
		one with write barriers, another without
		write barriers.  When JITted code ends up
		allocating a lot of memory while the GC is
		a copying GC, we would rather switch over to
		the generational mark and compact one.
		However the JITted code at that time may not
		have the required write barrier.
		We can only safely switch collectors when
		JITted code isn't running.
		*/

		static const size_t GENERATIONAL_TRIGGER_LEVEL =
			16384 * sizeof(Object::ref);
		static const size_t SSB_SIZE = 512;

		void* ssb_start;
		void* ssb_point;
		void* ssb_end;

		void* old_end;

		void generational_GC(size_t);

		/*determine if the specified address is in old space*/
		bool in_old(Object::ref*);

		std::set<Object::ref*> intergen;

	#endif

	void cheney_collection(Semispace*);
	void default_GC(size_t);

protected:
	ValueHolderRef other_spaces;
	void GC(size_t);

	void free_heap(void) {
		main.reset();
		other_spaces.reset(0);
	}

	/*required overload*/
	/*This virtual function *must* be defined by any derived
	class.  It should scan the root set of the heap.
	*/
	virtual void scan_root_object(GenericTraverser*) =0;

public:

	#ifndef ONLY_COPYING_GC
		Object::ref** acquire_ssb(void) const;
		void release_ssb(Object::ref**);

		friend Object::ref** __ssb_clean(Object::ref**);
	#endif

	template<class T>
	inline T* create(void) {
		/*compile-time checking that T inherits from Generic*/
		Generic* _create_template_must_be_Generic_ =
			static_cast<Generic*>((T*) 0);
		if(!main) throw std::bad_alloc();
		size_t sz = compute_size<T>();
		if(!main->can_fit(sz)) GC(sz);
		void* pt = main->alloc(sz);
		try {
			new(pt) T();
			return (T*) pt;
		} catch(...) {
			main->dealloc(pt);
			throw;
		}
	}
	template<class T>
	inline T* create_variadic(size_t extra) {
		/*TODO: consider splitting Generic hierarchy into two
		hierarchies inheriting from Generic, one hierarchy for
		non-variadic types, the other hierarchy for variadic
		types.
		*/
		Generic* _create_variadic_template_must_be_Generic_ =
			static_cast<Generic*>((T*) 0);
		if(!main) throw std::bad_alloc();
		size_t sz = compute_size_variadic<T>(extra);
		if(!main->can_fit(sz)) GC(sz);
		void* pt = main->alloc(sz);
		try {
			new(pt) T(extra);
			return (T*)pt;
		} catch(...) {
			main->dealloc(pt);
			throw;
		}
	}
	template<class T>
	inline T* lifo_create(void) {
		Generic* _lifo_create_template_must_be_Generic_ =
			static_cast<Generic*>((T*) 0);
		size_t sz = compute_size<T>();
		if(!main->can_fit(sz)) GC(sz);
		void* pt = main->lifo_alloc(sz);
		try {
			new(pt) T();
			return (T*) pt;
		} catch(...) {
			main->lifo_dealloc_abort(pt);
			throw;
		}
	}
	template<class T>
	inline T* lifo_create_variadic(size_t extra) {
		Generic* _lifo_create_variadic_template_must_be_Generic_ =
			static_cast<Generic*>((T*) 0);
		size_t sz = compute_size_variadic<T>(extra);
		if(!main->can_fit(sz)) GC(sz);
		void* pt = main->lifo_alloc(sz);
		try {
			new(pt) T(extra);
			return (T*)pt;
		} catch(...) {
			main->lifo_dealloc_abort(pt);
			throw;
		}
	}

	inline void lifo_dealloc(Generic* gp) {
		main->lifo_dealloc(gp);
	}

	void traverse_objects(HeapTraverser*) const;

	explicit Heap(size_t initsize = 8 * sizeof(Object::ref))
		: main(new Semispace(initsize)),
		tight(1)
		#ifndef ONLY_COPYING_GC
			, gc_type(gc_type_copying),
			recommended_gc_type(gc_type_copying),
			ssb_start(0),
			ssb_point(0),
			ssb_end(0),
			old_end(0),
			intergen()
		#endif
	{ }
        virtual ~Heap() {}
};

#endif //HEAPS_H

<commit_msg>inc/heaps.hpp: Changed Heap::in_old() to accept void*<commit_after>#ifndef HEAPS_H
#define HEAPS_H

#include"objects.hpp"
#include"generics.hpp"

#include<cstring>
#include<utility>
#include<set>

#include<boost/scoped_ptr.hpp>
#include<boost/noncopyable.hpp>

class Generic;
class ValueHolder;

void throw_DeallocError(void*);

/*-----------------------------------------------------------------------------
HeapTraverser
-----------------------------------------------------------------------------*/

class HeapTraverser {
public:
	virtual void traverse(Generic*) =0;
	virtual ~HeapTraverser(void) { }
};

/*
Utility class to wrap a GenericTraverser in a
HeapTraverser
*/
template<class T>
class ObjectsTraverser : public HeapTraverser {
private:
	T t;
public:
	template<class U>
	ObjectsTraverser(U u) : t(u) {
		GenericTraverser* ObjectsTraverser_template_should_derive_GenericTraverser
			= static_cast<GenericTraverser*>(&t);
	}
	template<class U, class V>
	ObjectsTraverser(U u, V v) : t(u, v) { }

	void traverse(Generic* gp) {
		gp->traverse_references(&t);
	}
};

/*-----------------------------------------------------------------------------
Semispaces
-----------------------------------------------------------------------------*/

class Semispace : boost::noncopyable {
private:
	void* mem;
	void* allocstart;
	void* allocpt;
	void* lifoallocstart;
	void* lifoallocpt;
	size_t prev_alloc;
	size_t max;
public:
	explicit Semispace(size_t);
	~Semispace();

	void* alloc(size_t);
	void dealloc(void*);

	void* lifo_alloc(size_t);
	void lifo_dealloc(Generic*);
	void lifo_dealloc_abort(void*);

	inline size_t size(void) const { return max; };
	inline size_t used(void) const {
		return (size_t)(((char*) allocpt) - ((char*) mem)) +
			(size_t)
			((((char*) mem) + max) - ((char*) lifoallocpt));
	};
	inline size_t free(void) const {
		return (size_t)(((char*) lifoallocpt) - ((char*) allocpt));
	}
	inline bool can_fit(size_t sz) const {
		return sz <= free();
	}

	void traverse_objects(HeapTraverser*) const;

	void clone(boost::scoped_ptr<Semispace>&, Generic*&) const;

	friend class Heap;
};

/*-----------------------------------------------------------------------------
ValueHolders
-----------------------------------------------------------------------------*/

class ValueHolder;

void throw_ValueHolderLinkingError(ValueHolder*);

class ValueHolder;
class LockedValueHolderRef;

/*smart pointer specifically for ValueHolder*/
class ValueHolderRef : boost::noncopyable {
private:
	ValueHolder* p;
public:
	ValueHolderRef(ValueHolder* np = 0) : p(np) { }
	/*don't define this here: just declare.
	The definition should occur after ValueHolder
	is completely declared.
	*/
	~ValueHolderRef();
	void reset(ValueHolder* np = 0);
	void swap(ValueHolderRef& n) {
		ValueHolder* tmp = p;
		p = n.p;
		n.p = tmp;
	}
	ValueHolder* operator->(void) { return p; }
	ValueHolder const* operator->(void) const { return p; }
	ValueHolder& operator*(void) { return *p; }
	ValueHolder const& operator*(void) const { return *p; }
	bool empty(void) const { return p == 0; }

	void insert(ValueHolderRef&);
	void remove(ValueHolderRef&);

	Object::ref value(void);

	friend class LockedValueHolderRef;
	friend class ValueHolder;
};

class ValueHolder {
private:
	boost::scoped_ptr<Semispace> sp;
	Object::ref val;

	/*
	Allow chaining, for example in
	the mailbox of a process.
	*/
	ValueHolderRef next;

	ValueHolder() { }
public:

	inline size_t used_total(void) const {
		size_t total = 0;
		for(ValueHolder const* pt = this; pt; pt = &*pt->next) {
			if(pt->sp) total += pt->sp->used();
		}
		return total;
	}

	void clone(ValueHolderRef&) const;

	void traverse_objects(HeapTraverser*) const;

	static void copy_object(ValueHolderRef&, Object::ref);

	friend class ValueHolderRef;
	friend class LockedValueHolderRef;
};

inline ValueHolderRef::~ValueHolderRef() {
	delete p;
}
inline void ValueHolderRef::reset(ValueHolder* np) {
	delete p;
	p = np;
}

inline void ValueHolderRef::insert(ValueHolderRef& o) {
	#ifdef DEBUG
		/*make sure there's exactly one element to insert*/
		if(!o.p) {
			throw_ValueHolderLinkingError(o.p);
		}
		if(o->next.p) {
			throw_ValueHolderLinkingError(o.p);
		}
	#endif
	o->next.p = p;
	p = o.p;
	o.p = 0; /*release*/
}

inline void ValueHolderRef::remove(ValueHolderRef& o) {
	#ifdef DEBUG
		if(o.p) {
			throw_ValueHolderLinkingError(o.p);
		}
	#endif
	if(!p) return;
	o.p = p;
	p = p->next.p;
	o->next.p = 0;
}

inline Object::ref ValueHolderRef::value(void) {
	if(p)	return p->val;
	else	return Object::nil();
}

/*-----------------------------------------------------------------------------
Heaps
-----------------------------------------------------------------------------*/

class Heap : boost::noncopyable {
private:
	boost::scoped_ptr<Semispace> main;
	bool tight;

	#ifndef ONLY_COPYING_GC
		enum GCType { gc_type_copying, gc_type_generational };
		GCType gc_type;

		/*Deferred GC type.*/
		GCType recommended_gc_type;
		/*
		We first decide to change GC types, but only
		assign to recommended_gc_type.  When the ssb
		is acquired, only then do we finalize the
		recommended type to the actual type.
		This is done in order to facilitate JIT.
		We can then JIT two versions of the code:
		one with write barriers, another without
		write barriers.  When JITted code ends up
		allocating a lot of memory while the GC is
		a copying GC, we would rather switch over to
		the generational mark and compact one.
		However the JITted code at that time may not
		have the required write barrier.
		We can only safely switch collectors when
		JITted code isn't running.
		*/

		static const size_t GENERATIONAL_TRIGGER_LEVEL =
			16384 * sizeof(Object::ref);
		static const size_t SSB_SIZE = 512;

		void* ssb_start;
		void* ssb_point;
		void* ssb_end;

		void* old_end;

		void generational_GC(size_t);

		/*determine if the specified address is in old space*/
		bool in_old(void*);

		std::set<Object::ref*> intergen;

	#endif

	void cheney_collection(Semispace*);
	void default_GC(size_t);

protected:
	ValueHolderRef other_spaces;
	void GC(size_t);

	void free_heap(void) {
		main.reset();
		other_spaces.reset(0);
	}

	/*required overload*/
	/*This virtual function *must* be defined by any derived
	class.  It should scan the root set of the heap.
	*/
	virtual void scan_root_object(GenericTraverser*) =0;

public:

	#ifndef ONLY_COPYING_GC
		Object::ref** acquire_ssb(void) const;
		void release_ssb(Object::ref**);

		friend Object::ref** __ssb_clean(Object::ref**);
	#endif

	template<class T>
	inline T* create(void) {
		/*compile-time checking that T inherits from Generic*/
		Generic* _create_template_must_be_Generic_ =
			static_cast<Generic*>((T*) 0);
		if(!main) throw std::bad_alloc();
		size_t sz = compute_size<T>();
		if(!main->can_fit(sz)) GC(sz);
		void* pt = main->alloc(sz);
		try {
			new(pt) T();
			return (T*) pt;
		} catch(...) {
			main->dealloc(pt);
			throw;
		}
	}
	template<class T>
	inline T* create_variadic(size_t extra) {
		/*TODO: consider splitting Generic hierarchy into two
		hierarchies inheriting from Generic, one hierarchy for
		non-variadic types, the other hierarchy for variadic
		types.
		*/
		Generic* _create_variadic_template_must_be_Generic_ =
			static_cast<Generic*>((T*) 0);
		if(!main) throw std::bad_alloc();
		size_t sz = compute_size_variadic<T>(extra);
		if(!main->can_fit(sz)) GC(sz);
		void* pt = main->alloc(sz);
		try {
			new(pt) T(extra);
			return (T*)pt;
		} catch(...) {
			main->dealloc(pt);
			throw;
		}
	}
	template<class T>
	inline T* lifo_create(void) {
		Generic* _lifo_create_template_must_be_Generic_ =
			static_cast<Generic*>((T*) 0);
		size_t sz = compute_size<T>();
		if(!main->can_fit(sz)) GC(sz);
		void* pt = main->lifo_alloc(sz);
		try {
			new(pt) T();
			return (T*) pt;
		} catch(...) {
			main->lifo_dealloc_abort(pt);
			throw;
		}
	}
	template<class T>
	inline T* lifo_create_variadic(size_t extra) {
		Generic* _lifo_create_variadic_template_must_be_Generic_ =
			static_cast<Generic*>((T*) 0);
		size_t sz = compute_size_variadic<T>(extra);
		if(!main->can_fit(sz)) GC(sz);
		void* pt = main->lifo_alloc(sz);
		try {
			new(pt) T(extra);
			return (T*)pt;
		} catch(...) {
			main->lifo_dealloc_abort(pt);
			throw;
		}
	}

	inline void lifo_dealloc(Generic* gp) {
		main->lifo_dealloc(gp);
	}

	void traverse_objects(HeapTraverser*) const;

	explicit Heap(size_t initsize = 8 * sizeof(Object::ref))
		: main(new Semispace(initsize)),
		tight(1)
		#ifndef ONLY_COPYING_GC
			, gc_type(gc_type_copying),
			recommended_gc_type(gc_type_copying),
			ssb_start(0),
			ssb_point(0),
			ssb_end(0),
			old_end(0),
			intergen()
		#endif
	{ }
        virtual ~Heap() {}
};

#endif //HEAPS_H

<|endoftext|>