<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 <memory>
#include <vector>

#include "cirq/google/api/v2/program.pb.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_quantum/core/ops/parse_context.h"
#include "tensorflow_quantum/core/ops/tfq_simulate_utils.h"
#include "tensorflow_quantum/core/proto/pauli_sum.pb.h"
#include "tensorflow_quantum/core/qsim/mux.h"
#include "tensorflow_quantum/core/qsim/state_space.h"
#include "tensorflow_quantum/core/src/circuit.h"
#include "tensorflow_quantum/core/src/circuit_parser.h"
#include "tensorflow_quantum/core/src/program_resolution.h"

namespace tfq {

using ::cirq::google::api::v2::Program;
using ::tensorflow::Status;
using ::tfq::proto::PauliSum;
using ::tfq::qsim_old::GetStateSpace;
using ::tfq::qsim_old::StateSpace;

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

  void Compute(tensorflow::OpKernelContext *context) override {
    // TODO (mbbrough): add more dimension checks for other inputs here.
    const int num_inputs = context->num_inputs();
    OP_REQUIRES(context, num_inputs == 5,
                tensorflow::errors::InvalidArgument(absl::StrCat(
                    "Expected 5 inputs, got ", num_inputs, " inputs.")));

    // Create the output Tensor.
    const int output_dim_batch_size = context->input(0).dim_size(0);
    const int output_dim_op_size = context->input(3).dim_size(1);
    tensorflow::TensorShape output_shape;
    output_shape.AddDim(output_dim_batch_size);
    output_shape.AddDim(output_dim_op_size);

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

    std::vector<Program> programs;
    std::vector<int> num_qubits;
    std::vector<std::vector<PauliSum>> pauli_sums;
    OP_REQUIRES_OK(context, GetProgramsAndNumQubits(context, &programs,
                                                    &num_qubits, &pauli_sums));

    std::vector<SymbolMap> maps;
    OP_REQUIRES_OK(context, GetSymbolMaps(context, &maps));

    OP_REQUIRES(context, pauli_sums.size() == programs.size(),
                tensorflow::errors::InvalidArgument(absl::StrCat(
                    "Number of circuits and PauliSums do not match. Got ",
                    programs.size(), " circuits and ", pauli_sums.size(),
                    " paulisums.")));

    std::vector<std::vector<int>> num_samples;
    OP_REQUIRES_OK(context, GetNumSamples(context, &num_samples));

    OP_REQUIRES(context, num_samples.size() == pauli_sums.size(),
                tensorflow::errors::InvalidArgument(absl::StrCat(
                    "Dimension 0 of num_samples and pauli_sums do not match.",
                    "Got ", num_samples.size(), " lists of sample sizes and ",
                    pauli_sums.size(), " lists of pauli sums.")));

    OP_REQUIRES(
        context, num_samples[0].size() == pauli_sums[0].size(),
        tensorflow::errors::InvalidArgument(absl::StrCat(
            "Dimension 1 of num_samples and pauli_sums do not match.", "Got ",
            num_samples[0].size(), " lists of sample sizes and ",
            pauli_sums[0].size(), " lists of pauli sums.")));

    auto DoWork = [&](int start, int end) {
      int old_batch_index = -2;
      int cur_batch_index = -1;
      int old_num_qubits = -2;
      int cur_op_index;
      std::unique_ptr<StateSpace> test_state = GetStateSpace(1, 1);
      std::unique_ptr<StateSpace> scratch_state = GetStateSpace(1, 1);
      for (int i = start; i < end; i++) {
        cur_batch_index = i / output_dim_op_size;
        cur_op_index = i % output_dim_op_size;

        // (#679) Just ignore empty program
        if (programs[cur_batch_index].circuit().moments().empty()) {
          output_tensor(cur_batch_index, cur_op_index) = -2.0;
          continue;
        }

        if (cur_batch_index != old_batch_index) {
          // We've run into a new wavefunction we must compute.
          // Only compute a new wavefunction when we have to.
          Program program = programs[cur_batch_index];
          const int num = num_qubits[cur_batch_index];
          OP_REQUIRES_OK(context,
                         ResolveSymbols(maps[cur_batch_index], &program));

          Circuit circuit;
          OP_REQUIRES_OK(context, CircuitFromProgram(program, num, &circuit));

          // TODO(mbbrough): Update this allocation hack so that a StateSpace
          //  object can grow it's memory dynamically to larger and larger size
          //  without ever having to call free (until very end). This is tricky
          //  to implement because right now certain statespaces can't simulate
          //  all states and we use StateSpaceSlow for smaller circuits.
          if (num != old_num_qubits) {
            test_state = GetStateSpace(num, 1);
            test_state->CreateState();

            // Also re-allocate scratch state for expectation calculations.
            scratch_state = GetStateSpace(num, 1);
            scratch_state->CreateState();
          }
          // no need to update scratch_state since ComputeExpectation
          // will take care of things for us.
          test_state->SetStateZero();
          OP_REQUIRES_OK(context, test_state->Update(circuit));
          old_num_qubits = num;
        }

        float expectation = 0.0;
        OP_REQUIRES_OK(
            context,
            test_state->ComputeSampledExpectation(
                pauli_sums[cur_batch_index][cur_op_index], scratch_state.get(),
                &expectation, num_samples[cur_batch_index][cur_op_index]));

        output_tensor(cur_batch_index, cur_op_index) = expectation;
        old_batch_index = cur_batch_index;
      }
    };

    const int block_size =
        GetBlockSize(context, output_dim_batch_size * output_dim_op_size);
    context->device()
        ->tensorflow_cpu_worker_threads()
        ->workers->TransformRangeConcurrently(
            block_size, output_dim_batch_size * output_dim_op_size, DoWork);
  }
};

REGISTER_KERNEL_BUILDER(
    Name("TfqSimulateSampledExpectation").Device(tensorflow::DEVICE_CPU),
    TfqSimulateSampledExpectationOp);

REGISTER_OP("TfqSimulateSampledExpectation")
    .Input("programs: string")
    .Input("symbol_names: string")
    .Input("symbol_values: float")
    .Input("pauli_sums: string")
    .Input("num_samples: int32")
    .Output("expectations: float")
    .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 pauli_sums_shape;
      TF_RETURN_IF_ERROR(c->WithRank(c->input(3), 2, &pauli_sums_shape));

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

      tensorflow::shape_inference::DimensionHandle output_rows =
          c->Dim(programs_shape, 0);
      tensorflow::shape_inference::DimensionHandle output_cols =
          c->Dim(pauli_sums_shape, 1);
      c->set_output(0, c->Matrix(output_rows, output_cols));

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

}  // namespace tfq
<commit_msg>Moved Sampled Expectation op to OSS qsim. (#303)<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 <memory>
#include <vector>

#include "../qsim/lib/circuit.h"
#include "../qsim/lib/gate_appl.h"
#include "../qsim/lib/gates_cirq.h"
#include "../qsim/lib/simmux.h"
#include "cirq/google/api/v2/program.pb.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_quantum/core/ops/parse_context.h"
#include "tensorflow_quantum/core/proto/pauli_sum.pb.h"
#include "tensorflow_quantum/core/src/util_qsim.h"

namespace tfq {

using ::cirq::google::api::v2::Program;
using ::tensorflow::Status;
using ::tfq::proto::PauliSum;

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

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

  void Compute(tensorflow::OpKernelContext *context) override {
    // TODO (mbbrough): add more dimension checks for other inputs here.
    const int num_inputs = context->num_inputs();
    OP_REQUIRES(context, num_inputs == 5,
                tensorflow::errors::InvalidArgument(absl::StrCat(
                    "Expected 5 inputs, got ", num_inputs, " inputs.")));

    // Create the output Tensor.
    const int output_dim_batch_size = context->input(0).dim_size(0);
    const int output_dim_op_size = context->input(3).dim_size(1);
    tensorflow::TensorShape output_shape;
    output_shape.AddDim(output_dim_batch_size);
    output_shape.AddDim(output_dim_op_size);

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

    std::vector<Program> programs;
    std::vector<int> num_qubits;
    std::vector<std::vector<PauliSum>> pauli_sums;
    OP_REQUIRES_OK(context, GetProgramsAndNumQubits(context, &programs,
                                                    &num_qubits, &pauli_sums));

    std::vector<SymbolMap> maps;
    OP_REQUIRES_OK(context, GetSymbolMaps(context, &maps));

    OP_REQUIRES(context, pauli_sums.size() == programs.size(),
                tensorflow::errors::InvalidArgument(absl::StrCat(
                    "Number of circuits and PauliSums do not match. Got ",
                    programs.size(), " circuits and ", pauli_sums.size(),
                    " paulisums.")));

    std::vector<std::vector<int>> num_samples;
    OP_REQUIRES_OK(context, GetNumSamples(context, &num_samples));

    OP_REQUIRES(context, num_samples.size() == pauli_sums.size(),
                tensorflow::errors::InvalidArgument(absl::StrCat(
                    "Dimension 0 of num_samples and pauli_sums do not match.",
                    "Got ", num_samples.size(), " lists of sample sizes and ",
                    pauli_sums.size(), " lists of pauli sums.")));

    OP_REQUIRES(
        context, num_samples[0].size() == pauli_sums[0].size(),
        tensorflow::errors::InvalidArgument(absl::StrCat(
            "Dimension 1 of num_samples and pauli_sums do not match.", "Got ",
            num_samples[0].size(), " lists of sample sizes and ",
            pauli_sums[0].size(), " lists of pauli sums.")));

    // Construct qsim circuits.
    std::vector<QsimCircuit> qsim_circuits(programs.size(), QsimCircuit());
    std::vector<std::vector<qsim::GateFused<QsimGate>>> fused_circuits(
        programs.size(), std::vector<qsim::GateFused<QsimGate>>({}));

    auto construct_f = [&](int start, int end) {
      for (int i = start; i < end; i++) {
        OP_REQUIRES_OK(context, QsimCircuitFromProgram(
                                    programs[i], maps[i], num_qubits[i],
                                    &qsim_circuits[i], &fused_circuits[i]));
      }
    };

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

    // Instantiate qsim objects.
    const auto tfq_for = tfq::QsimFor(context);
    using Simulator = qsim::Simulator<const tfq::QsimFor &>;
    using StateSpace = Simulator::StateSpace;
    using State = StateSpace::State;

    // Begin simulation.
    int largest_nq = 1;
    State sv = StateSpace(largest_nq, tfq_for).CreateState();
    State scratch = StateSpace(largest_nq, tfq_for).CreateState();

    // Simulate programs one by one. Parallelizing over wavefunctions
    // we no longer parallelize over circuits. Each time we encounter a
    // a larger circuit we will grow the Statevector as necessary.
    for (int i = 0; i < programs.size(); i++) {
      int nq = num_qubits[i];
      Simulator sim = Simulator(nq, tfq_for);
      StateSpace ss = StateSpace(nq, tfq_for);
      if (nq > largest_nq) {
        // need to switch to larger statespace.
        largest_nq = nq;
        sv = ss.CreateState();
        scratch = ss.CreateState();
      }
      // TODO: add heuristic here so that we do not always recompute
      //  the state if there is a possibility that circuit[i] and
      //  circuit[i + 1] produce the same state.
      ss.SetStateZero(sv);
      for (int j = 0; j < fused_circuits[i].size(); j++) {
        qsim::ApplyFusedGate(sim, fused_circuits[i][j], sv);
      }
      for (int j = 0; j < pauli_sums[i].size(); j++) {
        // (#679) Just ignore empty program
        if (programs[i].circuit().moments().empty()) {
          output_tensor(i, j) = -2.0;
          continue;
        }
        float exp_v = 0.0;
        OP_REQUIRES_OK(context, ComputeSampledExpectationQsim(
                                    pauli_sums[i][j], sim, ss, sv, scratch,
                                    num_samples[i][j], &exp_v));
        output_tensor(i, j) = exp_v;
      }
    }
    // just to be on the safe side.
    sv.release();
    scratch.release();
    qsim_circuits.clear();
    fused_circuits.clear();
    num_qubits.clear();
    maps.clear();
    pauli_sums.clear();
    num_samples.clear();
    programs.clear();
  }
};

REGISTER_KERNEL_BUILDER(
    Name("TfqSimulateSampledExpectation").Device(tensorflow::DEVICE_CPU),
    TfqSimulateSampledExpectationOp);

REGISTER_OP("TfqSimulateSampledExpectation")
    .Input("programs: string")
    .Input("symbol_names: string")
    .Input("symbol_values: float")
    .Input("pauli_sums: string")
    .Input("num_samples: int32")
    .Output("expectations: float")
    .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 pauli_sums_shape;
      TF_RETURN_IF_ERROR(c->WithRank(c->input(3), 2, &pauli_sums_shape));

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

      tensorflow::shape_inference::DimensionHandle output_rows =
          c->Dim(programs_shape, 0);
      tensorflow::shape_inference::DimensionHandle output_cols =
          c->Dim(pauli_sums_shape, 1);
      c->set_output(0, c->Matrix(output_rows, output_cols));

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

}  // namespace tfq
<|endoftext|>


<commit_before>/*******************************************************************************
 * thrill/data/channel.hpp
 *
 * Part of Project Thrill.
 *
 * Copyright (C) 2015 Timo Bingmann <tb@panthema.net>
 * Copyright (C) 2015 Tobias Sturm <mail@tobiassturm.de>
 *
 * This file has no license. Only Chuck Norris can compile it.
 ******************************************************************************/

#pragma once
#ifndef THRILL_DATA_CHANNEL_HEADER
#define THRILL_DATA_CHANNEL_HEADER

#include <thrill/common/stats_counter.hpp>
#include <thrill/common/stats_timer.hpp>
#include <thrill/data/block_queue.hpp>
#include <thrill/data/channel_sink.hpp>
#include <thrill/data/concat_block_source.hpp>
#include <thrill/data/file.hpp>
#include <thrill/data/multiplexer.hpp>
#include <thrill/data/multiplexer_header.hpp>
#include <thrill/net/connection.hpp>
#include <thrill/net/group.hpp>

#include <mutex>
#include <sstream>
#include <string>
#include <vector>

namespace thrill {
namespace data {

//! \addtogroup data Data Subsystem
//! \{

using ChannelId = size_t;

/*!
 * A Channel is a virtual set of connections to all other worker instances,
 * hence a "Channel" bundles them to a logical communication context. We call an
 * individual connection from a worker to another worker a "Host".
 *
 * To use a Channel, one can get a vector of BlockWriter via OpenWriters() of
 * outbound Channel. The vector is of size of workers in the system.
 * One can then write items destined to the
 * corresponding worker. The written items are buffered into a Block and only
 * sent when the Block is full. To force a send, use BlockWriter::Flush(). When
 * all items are sent, the BlockWriters **must** be closed using
 * BlockWriter::Close().
 *
 * To read the inbound Connection items, one can get a vector of BlockReader via
 * OpenReaders(), which can then be used to read items sent by individual
 * workers.
 *
 * Alternatively, one can use OpenReader() to get a BlockReader which delivers
 * all items from *all* worker in worker order (concatenating all inbound
 * Connections).
 *
 * As soon as all attached streams of the Channel have been Close() the number of
 * expected streams is reached, the channel is marked as finished and no more
 * data will arrive.
 */
class Channel
{
public:
    using BlockQueueSource = ConsumeBlockQueueSource;
    using BlockQueueReader = BlockReader<BlockQueueSource>;

    using ConcatBlockSource = data::ConcatBlockSource<DynBlockSource>;
    using ConcatBlockReader = BlockReader<ConcatBlockSource>;

    using Writer = DynBlockWriter;
    using Reader = BlockQueueReader;
    using ConcatReader = ConcatBlockReader;

    using StatsCounter = common::StatsCounter<size_t, common::g_enable_stats>;
    using StatsTimer = common::StatsTimer<common::g_enable_stats>;

    using ClosedCallback = std::function<void()>;

    //! Creates a new channel instance
    Channel(data::Multiplexer& multiplexer, const ChannelId& id,
            size_t my_local_worker_id)
        : tx_lifetime_(true), rx_lifetime_(true),
          tx_timespan_(), rx_timespan_(),
          id_(id),
          my_local_worker_id_(my_local_worker_id),
          multiplexer_(multiplexer),
          expected_closing_blocks_(
              (multiplexer_.num_hosts() - 1) * multiplexer_.num_workers_per_host_),
          received_closing_blocks_(0) {

        sinks_.reserve(multiplexer_.num_workers());
        queues_.reserve(multiplexer_.num_workers());

        // construct ChannelSink array
        for (size_t host = 0; host < multiplexer_.num_hosts(); ++host) {
            for (size_t worker = 0; worker < multiplexer_.num_workers_per_host_; worker++) {
                if (host == multiplexer_.my_host_rank()) {
                    sinks_.emplace_back(multiplexer_.block_pool_);
                }
                else {
                    sinks_.emplace_back(
                        multiplexer_.block_pool_,
                        &multiplexer_.dispatcher_,
                        &multiplexer_.group_.connection(host),
                        id,
                        multiplexer_.my_host_rank(), my_local_worker_id, worker,
                        &outgoing_bytes_, &outgoing_blocks_, &tx_timespan_);
                }
                // construct inbound queues
                queues_.emplace_back(multiplexer_.block_pool_);
            }
        }
    }

    //! non-copyable: delete copy-constructor
    Channel(const Channel&) = delete;
    //! non-copyable: delete assignment operator
    Channel& operator = (const Channel&) = delete;
    //! move-constructor: default
    Channel(Channel&&) = default;

    const ChannelId & id() const {
        return id_;
    }

    //! Creates BlockWriters for each worker. BlockWriter can only be opened
    //! once, otherwise the block sequence is incorrectly interleaved!
    std::vector<Writer> OpenWriters(size_t block_size = default_block_size) {
        tx_timespan_.StartEventually();

        std::vector<Writer> result;

        for (size_t host = 0; host < multiplexer_.num_hosts(); ++host) {
            for (size_t local_worker_id = 0; local_worker_id < multiplexer_.num_workers_per_host_; ++local_worker_id) {
                if (host == multiplexer_.my_host_rank()) {
                    auto target_queue_ptr = multiplexer_.loopback(id_, my_local_worker_id_, local_worker_id);
                    result.emplace_back(target_queue_ptr, block_size);
                }
                else {
                    size_t worker_id = host * multiplexer_.num_workers_per_host_ + local_worker_id;
                    result.emplace_back(&sinks_[worker_id], block_size);
                }
            }
        }

        assert(result.size() == multiplexer_.num_workers());
        return result;
    }

    //! Creates a BlockReader for each worker. The BlockReaders are attached to
    //! the BlockQueues in the Channel and wait for further Blocks to arrive or
    //! the Channel's remote close.
    std::vector<BlockQueueReader> OpenReaders() {
        rx_timespan_.StartEventually();

        std::vector<BlockQueueReader> result;

        for (size_t host = 0; host < multiplexer_.num_hosts(); ++host) {
            for (size_t local_worker_id = 0; local_worker_id < multiplexer_.num_workers_per_host_; ++local_worker_id) {
                size_t worker_id = host * multiplexer_.num_workers_per_host_ + local_worker_id;
                result.emplace_back(BlockQueueSource(queues_[worker_id]));
            }
        }

        assert(result.size() == multiplexer_.num_workers());
        return result;
    }

    //! Creates a BlockReader which concatenates items from all workers in
    //! worker rank order. The BlockReader is attached to one \ref
    //! ConcatBlockSource which includes all incoming queues of this channel.
    ConcatBlockReader OpenConcatReader(bool consume) {
        rx_timespan_.StartEventually();

        // construct vector of CachingBlockQueueSources to read from queues_.
        std::vector<DynBlockSource> result;
        for (size_t worker = 0; worker < multiplexer_.num_workers(); ++worker) {
            result.emplace_back(queues_[worker].GetBlockSource(consume));
        }
        // move BlockQueueSources into concatenation BlockSource, and to Reader.
        return ConcatBlockReader(ConcatBlockSource(std::move(result)));
    }

    /*!
     * Scatters a File to many worker
     *
     * elements from 0..offset[0] are sent to the first worker,
     * elements from (offset[0] + 1)..offset[1] are sent to the second worker.
     * elements from (offset[my_rank - 1] + 1)..(offset[my_rank]) are copied
     * The offset values range from 0..Manager::GetNumElements().
     * The number of given offsets must be equal to the net::Group::num_workers() * workers_per_host_.
     *
     * /param source File containing the data to be scattered.
     *
     * /param offsets - as described above. offsets.size must be equal to group.size
     */
    template <typename ItemType>
    void Scatter(const File& source, const std::vector<size_t>& offsets) {
        tx_timespan_.StartEventually();

        // current item offset in Reader
        size_t current = 0;
        File::KeepReader reader = source.GetKeepReader();

        std::vector<Writer> writers = OpenWriters();

        for (size_t worker = 0; worker < multiplexer_.num_workers(); ++worker) {
            // write [current,limit) to this worker
            size_t limit = offsets[worker];
            assert(current <= limit);
#if 0
            for ( ; current < limit; ++current) {
                assert(reader.HasNext());
                // move over one item (with deserialization and serialization)
                writers[worker](reader.template Next<ItemType>());
            }
#else
            if (current != limit) {
                writers[worker].AppendBlocks(
                    reader.template GetItemBatch<ItemType>(limit - current));
                current = limit;
            }
#endif
            writers[worker].Close();
        }

        tx_timespan_.Stop();
    }

    //! shuts the channel down.
    void Close() {
        // close all sinks, this should emit sentinel to all other worker.
        for (size_t i = 0; i != sinks_.size(); ++i) {
            if (sinks_[i].closed()) continue;
            sinks_[i].Close();
        }

        // close loop-back queue from this worker to itself
        auto my_global_worker_id = multiplexer_.my_host_rank() * multiplexer_.num_workers_per_host() + my_local_worker_id_;
        if (!queues_[my_global_worker_id].write_closed())
            queues_[my_global_worker_id].Close();

        // wait for close packets to arrive (this is a busy waiting loop, try to
        // do it better -tb)
        for (size_t i = 0; i != queues_.size(); ++i) {
            while (!queues_[i].write_closed()) {
                LOG << "wait for close from worker" << i;
                std::this_thread::sleep_for(std::chrono::milliseconds(10));
            }
        }
        tx_lifetime_.StopEventually();
        tx_timespan_.StopEventually();
        CallClosedCallbacksEventually();
    }

    void CallClosedCallbacksEventually() {
        std::lock_guard<std::mutex> lock(mutex_);
        if (closed()) {
            for (const auto& cb : closed_callbacks_)
                cb();
            closed_callbacks_.clear();
        }
    }

    //! Indicates if the channel is closed - meaning all remite streams have
    //! been closed. This does *not* include the loopback stream
    bool closed() const {
        bool closed = true;
        for (auto& q : queues_) {
            closed = closed && q.write_closed();
        }
        return closed;
    }

    //! Adds a Callback that is called when the channel is closed (r+w)
    void OnClose(ClosedCallback cb) {
        closed_callbacks_.push_back(cb);
    }

    ///////// expse these members - getters would be too java-ish /////////////

    //! StatsCounter for incoming data transfer
    //! Do not include loopback data transfer
    StatsCounter incoming_bytes_, incoming_blocks_;

    //! StatsCounters for outgoing data transfer - shared by all sinks
    //! Do not include loopback data transfer
    StatsCounter outgoing_bytes_, outgoing_blocks_;

    //! Timers from creation of channel until rx / tx direction is closed.
    StatsTimer tx_lifetime_, rx_lifetime_;

    //! Timers from first rx / tx package until rx / tx direction is closed.
    StatsTimer tx_timespan_, rx_timespan_;

    ///////////////////////////////////////////////////////////////////////////

protected:
    static const bool debug = false;

    ChannelId id_;

    size_t my_local_worker_id_;

    //! reference to multiplexer
    data::Multiplexer& multiplexer_;

    //! ChannelSink objects are receivers of Blocks outbound for other worker.
    std::vector<ChannelSink> sinks_;

    //! BlockQueues to store incoming Blocks with no attached destination.
    std::vector<BlockQueue> queues_;

    //! number of expected / received stream closing operations. Required to know when to
    //! stop rx_lifetime
    size_t expected_closing_blocks_, received_closing_blocks_;

    //! Callbacks that are called once when the channel is closed (r+w)
    std::vector<ClosedCallback> closed_callbacks_;

    // protects against race conditions in closed_callbacks_ loop
    std::mutex mutex_;

    //! for calling methods to deliver blocks
    friend class Multiplexer;

    //! called from Multiplexer when there is a new Block on a
    //! Channel.
    //! \param from the worker rank (host rank * num_workers/host + worker id)
    void OnChannelBlock(size_t from, Block&& b) {
        assert(from < queues_.size());
        rx_timespan_.StartEventually();
        incoming_bytes_ += b.size();
        incoming_blocks_++;

        sLOG << "OnChannelBlock" << b;

        if (debug) {
            sLOG << "channel" << id_ << "receive from" << from << ":"
                 << common::hexdump(b.ToString());
        }

        queues_[from].AppendBlock(b);
    }

    //! called from Multiplexer when a Channel closed notification was
    //! received.
    //! \param from the worker rank (host rank * num_workers/host + worker id)
    void OnCloseChannel(size_t from) {
        assert(from < queues_.size());
        assert(!queues_[from].write_closed());
        queues_[from].Close();
        if (expected_closing_blocks_ == ++received_closing_blocks_) {
            rx_lifetime_.StopEventually();
            rx_timespan_.StopEventually();
            CallClosedCallbacksEventually();
        }
    }

    //! Returns the loopback queue for the worker of this channel.
    BlockQueue * loopback_queue(size_t from_worker_id) {
        assert(from_worker_id < multiplexer_.num_workers_per_host_);
        size_t global_worker_rank = multiplexer_.num_workers_per_host_ * multiplexer_.my_host_rank() + from_worker_id;
        sLOG << "expose loopback queue for" << from_worker_id << "->" << my_local_worker_id_;
        return &(queues_[global_worker_rank]);
    }
};

using ChannelPtr = std::shared_ptr<Channel>;

/*! Simple structure that holds a all channel instances for the workers on the
 *! local host for a given channel id.
 */
class ChannelSet
{
public:
    //! Creates a ChannelSet with the given number of channels (num workers per host).
    ChannelSet(data::Multiplexer& multiplexer, ChannelId id, size_t num_workers_per_host) {
        for (size_t i = 0; i < num_workers_per_host; i++)
            channels_.push_back(std::make_shared<Channel>(multiplexer, id, i));
    }

    //! Returns the channel that will be consumed by the worker with the given
    //! local id
    ChannelPtr peer(size_t local_worker_id) {
        assert(local_worker_id < channels_.size());
        return channels_[local_worker_id];
    }

    void Close() {
        for (auto& c : channels_)
            c->Close();
    }

private:
    //! 'owns' all channels belonging to one channel id for all local workers.
    std::vector<ChannelPtr> channels_;
};

//! \}

} // namespace data
} // namespace thrill

#endif // !THRILL_DATA_CHANNEL_HEADER

/******************************************************************************/
<commit_msg>use StatLogger in data::Channel<commit_after>/*******************************************************************************
 * thrill/data/channel.hpp
 *
 * Part of Project Thrill.
 *
 * Copyright (C) 2015 Timo Bingmann <tb@panthema.net>
 * Copyright (C) 2015 Tobias Sturm <mail@tobiassturm.de>
 *
 * This file has no license. Only Chuck Norris can compile it.
 ******************************************************************************/

#pragma once
#ifndef THRILL_DATA_CHANNEL_HEADER
#define THRILL_DATA_CHANNEL_HEADER

#include <thrill/common/stats_counter.hpp>
#include <thrill/common/stats_timer.hpp>
#include <thrill/common/stat_logger.hpp>
#include <thrill/data/block_queue.hpp>
#include <thrill/data/channel_sink.hpp>
#include <thrill/data/concat_block_source.hpp>
#include <thrill/data/file.hpp>
#include <thrill/data/multiplexer.hpp>
#include <thrill/data/multiplexer_header.hpp>
#include <thrill/net/connection.hpp>
#include <thrill/net/group.hpp>

#include <mutex>
#include <sstream>
#include <string>
#include <vector>

namespace thrill {
namespace data {

//! \addtogroup data Data Subsystem
//! \{

using ChannelId = size_t;

/*!
 * A Channel is a virtual set of connections to all other worker instances,
 * hence a "Channel" bundles them to a logical communication context. We call an
 * individual connection from a worker to another worker a "Host".
 *
 * To use a Channel, one can get a vector of BlockWriter via OpenWriters() of
 * outbound Channel. The vector is of size of workers in the system.
 * One can then write items destined to the
 * corresponding worker. The written items are buffered into a Block and only
 * sent when the Block is full. To force a send, use BlockWriter::Flush(). When
 * all items are sent, the BlockWriters **must** be closed using
 * BlockWriter::Close().
 *
 * To read the inbound Connection items, one can get a vector of BlockReader via
 * OpenReaders(), which can then be used to read items sent by individual
 * workers.
 *
 * Alternatively, one can use OpenReader() to get a BlockReader which delivers
 * all items from *all* worker in worker order (concatenating all inbound
 * Connections).
 *
 * As soon as all attached streams of the Channel have been Close() the number of
 * expected streams is reached, the channel is marked as finished and no more
 * data will arrive.
 */
class Channel
{
public:
    using BlockQueueSource = ConsumeBlockQueueSource;
    using BlockQueueReader = BlockReader<BlockQueueSource>;

    using ConcatBlockSource = data::ConcatBlockSource<DynBlockSource>;
    using ConcatBlockReader = BlockReader<ConcatBlockSource>;

    using Writer = DynBlockWriter;
    using Reader = BlockQueueReader;
    using ConcatReader = ConcatBlockReader;

    using StatsCounter = common::StatsCounter<size_t, common::g_enable_stats>;
    using StatsTimer = common::StatsTimer<common::g_enable_stats>;

    using ClosedCallback = std::function<void()>;

    //! Creates a new channel instance
    Channel(data::Multiplexer& multiplexer, const ChannelId& id,
            size_t my_local_worker_id)
        : tx_lifetime_(true), rx_lifetime_(true),
          tx_timespan_(), rx_timespan_(),
          id_(id),
          my_local_worker_id_(my_local_worker_id),
          multiplexer_(multiplexer),
          expected_closing_blocks_(
              (multiplexer_.num_hosts() - 1) * multiplexer_.num_workers_per_host_),
          received_closing_blocks_(0) {

        sinks_.reserve(multiplexer_.num_workers());
        queues_.reserve(multiplexer_.num_workers());

        // construct ChannelSink array
        for (size_t host = 0; host < multiplexer_.num_hosts(); ++host) {
            for (size_t worker = 0; worker < multiplexer_.num_workers_per_host_; worker++) {
                if (host == multiplexer_.my_host_rank()) {
                    sinks_.emplace_back(multiplexer_.block_pool_);
                }
                else {
                    sinks_.emplace_back(
                        multiplexer_.block_pool_,
                        &multiplexer_.dispatcher_,
                        &multiplexer_.group_.connection(host),
                        id,
                        multiplexer_.my_host_rank(), my_local_worker_id, worker,
                        &outgoing_bytes_, &outgoing_blocks_, &tx_timespan_);
                }
                // construct inbound queues
                queues_.emplace_back(multiplexer_.block_pool_);
            }
        }
    }

    //! non-copyable: delete copy-constructor
    Channel(const Channel&) = delete;
    //! non-copyable: delete assignment operator
    Channel& operator = (const Channel&) = delete;
    //! move-constructor: default
    Channel(Channel&&) = default;

    const ChannelId & id() const {
        return id_;
    }

    //! Creates BlockWriters for each worker. BlockWriter can only be opened
    //! once, otherwise the block sequence is incorrectly interleaved!
    std::vector<Writer> OpenWriters(size_t block_size = default_block_size) {
        tx_timespan_.StartEventually();

        std::vector<Writer> result;

        for (size_t host = 0; host < multiplexer_.num_hosts(); ++host) {
            for (size_t local_worker_id = 0; local_worker_id < multiplexer_.num_workers_per_host_; ++local_worker_id) {
                if (host == multiplexer_.my_host_rank()) {
                    auto target_queue_ptr = multiplexer_.loopback(id_, my_local_worker_id_, local_worker_id);
                    result.emplace_back(target_queue_ptr, block_size);
                }
                else {
                    size_t worker_id = host * multiplexer_.num_workers_per_host_ + local_worker_id;
                    result.emplace_back(&sinks_[worker_id], block_size);
                }
            }
        }

        assert(result.size() == multiplexer_.num_workers());
        return result;
    }

    //! Creates a BlockReader for each worker. The BlockReaders are attached to
    //! the BlockQueues in the Channel and wait for further Blocks to arrive or
    //! the Channel's remote close.
    std::vector<BlockQueueReader> OpenReaders() {
        rx_timespan_.StartEventually();

        std::vector<BlockQueueReader> result;

        for (size_t host = 0; host < multiplexer_.num_hosts(); ++host) {
            for (size_t local_worker_id = 0; local_worker_id < multiplexer_.num_workers_per_host_; ++local_worker_id) {
                size_t worker_id = host * multiplexer_.num_workers_per_host_ + local_worker_id;
                result.emplace_back(BlockQueueSource(queues_[worker_id]));
            }
        }

        assert(result.size() == multiplexer_.num_workers());
        return result;
    }

    //! Creates a BlockReader which concatenates items from all workers in
    //! worker rank order. The BlockReader is attached to one \ref
    //! ConcatBlockSource which includes all incoming queues of this channel.
    ConcatBlockReader OpenConcatReader(bool consume) {
        rx_timespan_.StartEventually();

        // construct vector of CachingBlockQueueSources to read from queues_.
        std::vector<DynBlockSource> result;
        for (size_t worker = 0; worker < multiplexer_.num_workers(); ++worker) {
            result.emplace_back(queues_[worker].GetBlockSource(consume));
        }
        // move BlockQueueSources into concatenation BlockSource, and to Reader.
        return ConcatBlockReader(ConcatBlockSource(std::move(result)));
    }

    /*!
     * Scatters a File to many worker
     *
     * elements from 0..offset[0] are sent to the first worker,
     * elements from (offset[0] + 1)..offset[1] are sent to the second worker.
     * elements from (offset[my_rank - 1] + 1)..(offset[my_rank]) are copied
     * The offset values range from 0..Manager::GetNumElements().
     * The number of given offsets must be equal to the net::Group::num_workers() * workers_per_host_.
     *
     * /param source File containing the data to be scattered.
     *
     * /param offsets - as described above. offsets.size must be equal to group.size
     */
    template <typename ItemType>
    void Scatter(const File& source, const std::vector<size_t>& offsets) {
        tx_timespan_.StartEventually();

        // current item offset in Reader
        size_t current = 0;
        File::KeepReader reader = source.GetKeepReader();

        std::vector<Writer> writers = OpenWriters();

        for (size_t worker = 0; worker < multiplexer_.num_workers(); ++worker) {
            // write [current,limit) to this worker
            size_t limit = offsets[worker];
            assert(current <= limit);
#if 0
            for ( ; current < limit; ++current) {
                assert(reader.HasNext());
                // move over one item (with deserialization and serialization)
                writers[worker](reader.template Next<ItemType>());
            }
#else
            if (current != limit) {
                writers[worker].AppendBlocks(
                    reader.template GetItemBatch<ItemType>(limit - current));
                current = limit;
            }
#endif
            writers[worker].Close();
        }

        tx_timespan_.Stop();
        STAT_NO_RANK << "worker_id" << my_global_worker_id()
                     << "event" << "scatter_send_done"
                     << "channel_id" << id_
                     << "tx_time_μs" << tx_timespan_.Microseconds()
                     << "tx_lifetime_μs" << tx_lifetime_.Microseconds()
                     << "bytes_sent" << outgoing_bytes_
                     << "blocks_sent" << outgoing_blocks_;
    }

    //! shuts the channel down.
    void Close() {
        // close all sinks, this should emit sentinel to all other worker.
        for (size_t i = 0; i != sinks_.size(); ++i) {
            if (sinks_[i].closed()) continue;
            sinks_[i].Close();
        }

        // close loop-back queue from this worker to itself
        if (!queues_[my_global_worker_id()].write_closed())
            queues_[my_global_worker_id()].Close();

        // wait for close packets to arrive (this is a busy waiting loop, try to
        // do it better -tb)
        for (size_t i = 0; i != queues_.size(); ++i) {
            while (!queues_[i].write_closed()) {
                LOG << "wait for close from worker" << i;
                std::this_thread::sleep_for(std::chrono::milliseconds(10));
            }
        }
        tx_lifetime_.StopEventually();
        tx_timespan_.StopEventually();
        CallClosedCallbacksEventually();
    }

    void CallClosedCallbacksEventually() {
        std::lock_guard<std::mutex> lock(mutex_);
        if (closed()) {
            for (const auto& cb : closed_callbacks_)
                cb();
            closed_callbacks_.clear();
        }
    }

    //! Indicates if the channel is closed - meaning all remite streams have
    //! been closed. This does *not* include the loopback stream
    bool closed() const {
        bool closed = true;
        for (auto& q : queues_) {
            closed = closed && q.write_closed();
        }
        return closed;
    }

    //! Adds a Callback that is called when the channel is closed (r+w)
    void OnClose(ClosedCallback cb) {
        closed_callbacks_.push_back(cb);
    }

    ///////// expse these members - getters would be too java-ish /////////////

    //! StatsCounter for incoming data transfer
    //! Do not include loopback data transfer
    StatsCounter incoming_bytes_, incoming_blocks_;

    //! StatsCounters for outgoing data transfer - shared by all sinks
    //! Do not include loopback data transfer
    StatsCounter outgoing_bytes_, outgoing_blocks_;

    //! Timers from creation of channel until rx / tx direction is closed.
    StatsTimer tx_lifetime_, rx_lifetime_;

    //! Timers from first rx / tx package until rx / tx direction is closed.
    StatsTimer tx_timespan_, rx_timespan_;

    ///////////////////////////////////////////////////////////////////////////

protected:
    static const bool debug = false;

    ChannelId id_;

    size_t my_local_worker_id_;

    //! reference to multiplexer
    data::Multiplexer& multiplexer_;

    //! ChannelSink objects are receivers of Blocks outbound for other worker.
    std::vector<ChannelSink> sinks_;

    //! BlockQueues to store incoming Blocks with no attached destination.
    std::vector<BlockQueue> queues_;

    //! number of expected / received stream closing operations. Required to know when to
    //! stop rx_lifetime
    size_t expected_closing_blocks_, received_closing_blocks_;

    //! Callbacks that are called once when the channel is closed (r+w)
    std::vector<ClosedCallback> closed_callbacks_;

    // protects against race conditions in closed_callbacks_ loop
    std::mutex mutex_;

    //! for calling methods to deliver blocks
    friend class Multiplexer;

    //! called from Multiplexer when there is a new Block on a
    //! Channel.
    //! \param from the worker rank (host rank * num_workers/host + worker id)
    void OnChannelBlock(size_t from, Block&& b) {
        assert(from < queues_.size());
        rx_timespan_.StartEventually();
        incoming_bytes_ += b.size();
        incoming_blocks_++;

        sLOG << "OnChannelBlock" << b;

        if (debug) {
            sLOG << "channel" << id_ << "receive from" << from << ":"
                 << common::hexdump(b.ToString());
        }

        queues_[from].AppendBlock(b);
    }

    //! called from Multiplexer when a Channel closed notification was
    //! received.
    //! \param from the worker rank (host rank * num_workers/host + worker id)
    void OnCloseChannel(size_t from) {
        assert(from < queues_.size());
        assert(!queues_[from].write_closed());
        queues_[from].Close();
        if (expected_closing_blocks_ == ++received_closing_blocks_) {
            rx_lifetime_.StopEventually();
            rx_timespan_.StopEventually();
            STAT_NO_RANK << "worker_id" << my_global_worker_id()
                        << "event" << "channel_closed"
                        << "channel_id" << id_
                        << "tx_time_μs" << tx_timespan_.Microseconds()
                        << "rx_time_μs" << rx_timespan_.Microseconds()
                        << "tx_lifetime_μs" << tx_lifetime_.Microseconds()
                        << "rx_lifetime_μs" << rx_lifetime_.Microseconds()
                        << "bytes_sent" << outgoing_bytes_
                        << "blocks_sent" << outgoing_blocks_
                        << "bytes_incoming" << incoming_bytes_
                        << "blocks_sent" << incoming_blocks_;
            CallClosedCallbacksEventually();
        }
    }

    //! Returns the loopback queue for the worker of this channel.
    BlockQueue * loopback_queue(size_t from_worker_id) {
        assert(from_worker_id < multiplexer_.num_workers_per_host_);
        size_t global_worker_rank = multiplexer_.num_workers_per_host_ * multiplexer_.my_host_rank() + from_worker_id;
        sLOG << "expose loopback queue for" << from_worker_id << "->" << my_local_worker_id_;
        return &(queues_[global_worker_rank]);
    }
private:
    //TODO maybe use context as memer reference instead ?
    size_t my_global_worker_id() const {
        return multiplexer_.my_host_rank() * multiplexer_.num_workers_per_host() + my_local_worker_id_;
    }
};

using ChannelPtr = std::shared_ptr<Channel>;

/*! Simple structure that holds a all channel instances for the workers on the
 *! local host for a given channel id.
 */
class ChannelSet
{
public:
    //! Creates a ChannelSet with the given number of channels (num workers per host).
    ChannelSet(data::Multiplexer& multiplexer, ChannelId id, size_t num_workers_per_host) {
        for (size_t i = 0; i < num_workers_per_host; i++)
            channels_.push_back(std::make_shared<Channel>(multiplexer, id, i));
    }

    //! Returns the channel that will be consumed by the worker with the given
    //! local id
    ChannelPtr peer(size_t local_worker_id) {
        assert(local_worker_id < channels_.size());
        return channels_[local_worker_id];
    }

    void Close() {
        for (auto& c : channels_)
            c->Close();
    }

private:
    //! 'owns' all channels belonging to one channel id for all local workers.
    std::vector<ChannelPtr> channels_;
};

//! \}

} // namespace data
} // namespace thrill

#endif // !THRILL_DATA_CHANNEL_HEADER

/******************************************************************************/
<|endoftext|>


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

// Test of FieldTrial class

#include "base/field_trial.h"

#include "base/stringprintf.h"
#include "testing/gtest/include/gtest/gtest.h"

class FieldTrialTest : public testing::Test {
 public:
  FieldTrialTest() : trial_list_() { }

 private:
  FieldTrialList trial_list_;
};

// Test registration, and also check that destructors are called for trials
// (and that Purify doesn't catch us leaking).
TEST_F(FieldTrialTest, Registration) {
  const char* name1 = "name 1 test";
  const char* name2 = "name 2 test";
  EXPECT_FALSE(FieldTrialList::Find(name1));
  EXPECT_FALSE(FieldTrialList::Find(name2));

  FieldTrial* trial1 = new FieldTrial(name1, 10);
  EXPECT_EQ(FieldTrial::kNotParticipating, trial1->group());
  EXPECT_EQ(name1, trial1->name());
  EXPECT_EQ("", trial1->group_name());

  trial1->AppendGroup("", 7);

  EXPECT_EQ(trial1, FieldTrialList::Find(name1));
  EXPECT_FALSE(FieldTrialList::Find(name2));

  FieldTrial* trial2 = new FieldTrial(name2, 10);
  EXPECT_EQ(FieldTrial::kNotParticipating, trial2->group());
  EXPECT_EQ(name2, trial2->name());
  EXPECT_EQ("", trial2->group_name());

  trial2->AppendGroup("a first group", 7);

  EXPECT_EQ(trial1, FieldTrialList::Find(name1));
  EXPECT_EQ(trial2, FieldTrialList::Find(name2));
  // Note: FieldTrialList should delete the objects at shutdown.
}

TEST_F(FieldTrialTest, AbsoluteProbabilities) {
  char always_true[] = " always true";
  char always_false[] = " always false";
  for (int i = 1; i < 250; ++i) {
    // Try lots of names, by changing the first character of the name.
    always_true[0] = i;
    always_false[0] = i;

    FieldTrial* trial_true = new FieldTrial(always_true, 10);
    const std::string winner = "TheWinner";
    int winner_group = trial_true->AppendGroup(winner, 10);

    EXPECT_EQ(winner_group, trial_true->group());
    EXPECT_EQ(winner, trial_true->group_name());

    FieldTrial* trial_false = new FieldTrial(always_false, 10);
    int loser_group = trial_false->AppendGroup("ALoser", 0);

    EXPECT_NE(loser_group, trial_false->group());
  }
}

TEST_F(FieldTrialTest, RemainingProbability) {
  // First create a test that hasn't had a winner yet.
  const std::string winner = "Winner";
  const std::string loser = "Loser";
  scoped_refptr<FieldTrial> trial;
  int counter = 0;
  do {
    std::string name = base::StringPrintf("trial%d", ++counter);
    trial = new FieldTrial(name, 10);
    trial->AppendGroup(loser, 5);  // 50% chance of not being chosen.
  } while (trial->group() != FieldTrial::kNotParticipating);

  // Now add a winner with all remaining probability.
  trial->AppendGroup(winner, FieldTrial::kAllRemainingProbability);

  // And that winner should ALWAYS win.
  EXPECT_EQ(winner, trial->group_name());
}

TEST_F(FieldTrialTest, MiddleProbabilities) {
  char name[] = " same name";
  bool false_event_seen = false;
  bool true_event_seen = false;
  for (int i = 1; i < 250; ++i) {
    name[0] = i;
    FieldTrial* trial = new FieldTrial(name, 10);
    int might_win = trial->AppendGroup("MightWin", 5);

    if (trial->group() == might_win) {
      true_event_seen = true;
    } else {
      false_event_seen = true;
    }
    if (false_event_seen && true_event_seen)
      return;  // Successful test!!!
  }
  // Very surprising to get here. Probability should be around 1 in 2 ** 250.
  // One of the following will fail.
  EXPECT_TRUE(false_event_seen);
  EXPECT_TRUE(true_event_seen);
}

TEST_F(FieldTrialTest, OneWinner) {
  char name[] = "Some name";
  int group_count(10);

  FieldTrial* trial = new FieldTrial(name, group_count);
  int winner_index(-2);
  std::string winner_name;

  for (int i = 1; i <= group_count; ++i) {
    int might_win = trial->AppendGroup("", 1);

    if (trial->group() == might_win) {
      EXPECT_EQ(-2, winner_index);
      winner_index = might_win;
      StringAppendF(&winner_name, "%d", might_win);
      EXPECT_EQ(winner_name, trial->group_name());
    }
  }
  EXPECT_GE(winner_index, 0);
  EXPECT_EQ(trial->group(), winner_index);
  EXPECT_EQ(trial->group_name(), winner_name);
}

TEST_F(FieldTrialTest, Save) {
  std::string save_string;

  FieldTrial* trial = new FieldTrial("Some name", 10);
  // There is no winner yet, so no textual group name is associated with trial.
  EXPECT_EQ("", trial->group_name());
  FieldTrialList::StatesToString(&save_string);
  EXPECT_EQ("", save_string);
  save_string.clear();

  // Create a winning group.
  trial->AppendGroup("Winner", 10);
  FieldTrialList::StatesToString(&save_string);
  EXPECT_EQ("Some name/Winner/", save_string);
  save_string.clear();

  // Create a second trial and winning group.
  FieldTrial* trial2 = new FieldTrial("xxx", 10);
  trial2->AppendGroup("yyyy", 10);

  FieldTrialList::StatesToString(&save_string);
  // We assume names are alphabetized... though this is not critical.
  EXPECT_EQ("Some name/Winner/xxx/yyyy/", save_string);
}

TEST_F(FieldTrialTest, Restore) {
  EXPECT_TRUE(FieldTrialList::Find("Some_name") == NULL);
  EXPECT_TRUE(FieldTrialList::Find("xxx") == NULL);

  FieldTrialList::StringAugmentsState("Some_name/Winner/xxx/yyyy/");

  FieldTrial* trial = FieldTrialList::Find("Some_name");
  ASSERT_NE(static_cast<FieldTrial*>(NULL), trial);
  EXPECT_EQ("Winner", trial->group_name());
  EXPECT_EQ("Some_name", trial->name());

  trial = FieldTrialList::Find("xxx");
  ASSERT_NE(static_cast<FieldTrial*>(NULL), trial);
  EXPECT_EQ("yyyy", trial->group_name());
  EXPECT_EQ("xxx", trial->name());
}

TEST_F(FieldTrialTest, BogusRestore) {
  EXPECT_FALSE(FieldTrialList::StringAugmentsState("MissingSlash"));
  EXPECT_FALSE(FieldTrialList::StringAugmentsState("MissingGroupName/"));
  EXPECT_FALSE(FieldTrialList::StringAugmentsState("MissingFinalSlash/gname"));
  EXPECT_FALSE(FieldTrialList::StringAugmentsState("/noname, only group/"));
}

TEST_F(FieldTrialTest, DuplicateRestore) {
  FieldTrial* trial = new FieldTrial("Some name", 10);
  trial->AppendGroup("Winner", 10);
  std::string save_string;
  FieldTrialList::StatesToString(&save_string);
  EXPECT_EQ("Some name/Winner/", save_string);

  // It is OK if we redundantly specify a winner.
  EXPECT_TRUE(FieldTrialList::StringAugmentsState(save_string));

  // But it is an error to try to change to a different winner.
  EXPECT_FALSE(FieldTrialList::StringAugmentsState("Some name/Loser/"));
}

TEST_F(FieldTrialTest, MakeName) {
  FieldTrial* trial = new FieldTrial("Field Trial", 10);
  trial->AppendGroup("Winner", 10);
  EXPECT_EQ("Histogram_Winner",
            FieldTrial::MakeName("Histogram", "Field Trial"));
}
<commit_msg>Add extra unit test to avoid off-by-one errors<commit_after>// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Test of FieldTrial class

#include "base/field_trial.h"

#include "base/stringprintf.h"
#include "testing/gtest/include/gtest/gtest.h"

class FieldTrialTest : public testing::Test {
 public:
  FieldTrialTest() : trial_list_() { }

 private:
  FieldTrialList trial_list_;
};

// Test registration, and also check that destructors are called for trials
// (and that Purify doesn't catch us leaking).
TEST_F(FieldTrialTest, Registration) {
  const char* name1 = "name 1 test";
  const char* name2 = "name 2 test";
  EXPECT_FALSE(FieldTrialList::Find(name1));
  EXPECT_FALSE(FieldTrialList::Find(name2));

  FieldTrial* trial1 = new FieldTrial(name1, 10);
  EXPECT_EQ(FieldTrial::kNotParticipating, trial1->group());
  EXPECT_EQ(name1, trial1->name());
  EXPECT_EQ("", trial1->group_name());

  trial1->AppendGroup("", 7);

  EXPECT_EQ(trial1, FieldTrialList::Find(name1));
  EXPECT_FALSE(FieldTrialList::Find(name2));

  FieldTrial* trial2 = new FieldTrial(name2, 10);
  EXPECT_EQ(FieldTrial::kNotParticipating, trial2->group());
  EXPECT_EQ(name2, trial2->name());
  EXPECT_EQ("", trial2->group_name());

  trial2->AppendGroup("a first group", 7);

  EXPECT_EQ(trial1, FieldTrialList::Find(name1));
  EXPECT_EQ(trial2, FieldTrialList::Find(name2));
  // Note: FieldTrialList should delete the objects at shutdown.
}

TEST_F(FieldTrialTest, AbsoluteProbabilities) {
  char always_true[] = " always true";
  char always_false[] = " always false";
  for (int i = 1; i < 250; ++i) {
    // Try lots of names, by changing the first character of the name.
    always_true[0] = i;
    always_false[0] = i;

    FieldTrial* trial_true = new FieldTrial(always_true, 10);
    const std::string winner = "TheWinner";
    int winner_group = trial_true->AppendGroup(winner, 10);

    EXPECT_EQ(winner_group, trial_true->group());
    EXPECT_EQ(winner, trial_true->group_name());

    FieldTrial* trial_false = new FieldTrial(always_false, 10);
    int loser_group = trial_false->AppendGroup("ALoser", 0);

    EXPECT_NE(loser_group, trial_false->group());
  }
}

TEST_F(FieldTrialTest, RemainingProbability) {
  // First create a test that hasn't had a winner yet.
  const std::string winner = "Winner";
  const std::string loser = "Loser";
  scoped_refptr<FieldTrial> trial;
  int counter = 0;
  do {
    std::string name = base::StringPrintf("trial%d", ++counter);
    trial = new FieldTrial(name, 10);
    trial->AppendGroup(loser, 5);  // 50% chance of not being chosen.
  } while (trial->group() != FieldTrial::kNotParticipating);

  // Now add a winner with all remaining probability.
  trial->AppendGroup(winner, FieldTrial::kAllRemainingProbability);

  // And that winner should ALWAYS win.
  EXPECT_EQ(winner, trial->group_name());
}

TEST_F(FieldTrialTest, FiftyFiftyProbability) {
  // Check that even with small divisors, we have the proper probabilities, and
  // all outcomes are possible.  Since this is a 50-50 test, it should get both
  // outcomes in a few tries, but we'll try no more than 100 times (and be flaky
  // with probability around 1 in 2^99).
  bool first_winner = false;
  bool second_winner = false;
  int counter = 0;
  do {
    std::string name = base::StringPrintf("FiftyFifty%d", ++counter);
    scoped_refptr<FieldTrial> trial = new FieldTrial(name, 2);
    trial->AppendGroup("first", 1);  // 50% chance of being chosen.
    if (trial->group() != FieldTrial::kNotParticipating) {
      first_winner = true;
      continue;
    }
    trial->AppendGroup("second", 1);  // Always chosen at this point.
    EXPECT_NE(FieldTrial::kNotParticipating, trial->group());
    second_winner = true;
  } while ((!second_winner || !first_winner) && counter < 100);
  EXPECT_TRUE(second_winner);
  EXPECT_TRUE(first_winner);
}

TEST_F(FieldTrialTest, MiddleProbabilities) {
  char name[] = " same name";
  bool false_event_seen = false;
  bool true_event_seen = false;
  for (int i = 1; i < 250; ++i) {
    name[0] = i;
    FieldTrial* trial = new FieldTrial(name, 10);
    int might_win = trial->AppendGroup("MightWin", 5);

    if (trial->group() == might_win) {
      true_event_seen = true;
    } else {
      false_event_seen = true;
    }
    if (false_event_seen && true_event_seen)
      return;  // Successful test!!!
  }
  // Very surprising to get here. Probability should be around 1 in 2 ** 250.
  // One of the following will fail.
  EXPECT_TRUE(false_event_seen);
  EXPECT_TRUE(true_event_seen);
}

TEST_F(FieldTrialTest, OneWinner) {
  char name[] = "Some name";
  int group_count(10);

  FieldTrial* trial = new FieldTrial(name, group_count);
  int winner_index(-2);
  std::string winner_name;

  for (int i = 1; i <= group_count; ++i) {
    int might_win = trial->AppendGroup("", 1);

    if (trial->group() == might_win) {
      EXPECT_EQ(-2, winner_index);
      winner_index = might_win;
      StringAppendF(&winner_name, "%d", might_win);
      EXPECT_EQ(winner_name, trial->group_name());
    }
  }
  EXPECT_GE(winner_index, 0);
  EXPECT_EQ(trial->group(), winner_index);
  EXPECT_EQ(trial->group_name(), winner_name);
}

TEST_F(FieldTrialTest, Save) {
  std::string save_string;

  FieldTrial* trial = new FieldTrial("Some name", 10);
  // There is no winner yet, so no textual group name is associated with trial.
  EXPECT_EQ("", trial->group_name());
  FieldTrialList::StatesToString(&save_string);
  EXPECT_EQ("", save_string);
  save_string.clear();

  // Create a winning group.
  trial->AppendGroup("Winner", 10);
  FieldTrialList::StatesToString(&save_string);
  EXPECT_EQ("Some name/Winner/", save_string);
  save_string.clear();

  // Create a second trial and winning group.
  FieldTrial* trial2 = new FieldTrial("xxx", 10);
  trial2->AppendGroup("yyyy", 10);

  FieldTrialList::StatesToString(&save_string);
  // We assume names are alphabetized... though this is not critical.
  EXPECT_EQ("Some name/Winner/xxx/yyyy/", save_string);
}

TEST_F(FieldTrialTest, Restore) {
  EXPECT_TRUE(FieldTrialList::Find("Some_name") == NULL);
  EXPECT_TRUE(FieldTrialList::Find("xxx") == NULL);

  FieldTrialList::StringAugmentsState("Some_name/Winner/xxx/yyyy/");

  FieldTrial* trial = FieldTrialList::Find("Some_name");
  ASSERT_NE(static_cast<FieldTrial*>(NULL), trial);
  EXPECT_EQ("Winner", trial->group_name());
  EXPECT_EQ("Some_name", trial->name());

  trial = FieldTrialList::Find("xxx");
  ASSERT_NE(static_cast<FieldTrial*>(NULL), trial);
  EXPECT_EQ("yyyy", trial->group_name());
  EXPECT_EQ("xxx", trial->name());
}

TEST_F(FieldTrialTest, BogusRestore) {
  EXPECT_FALSE(FieldTrialList::StringAugmentsState("MissingSlash"));
  EXPECT_FALSE(FieldTrialList::StringAugmentsState("MissingGroupName/"));
  EXPECT_FALSE(FieldTrialList::StringAugmentsState("MissingFinalSlash/gname"));
  EXPECT_FALSE(FieldTrialList::StringAugmentsState("/noname, only group/"));
}

TEST_F(FieldTrialTest, DuplicateRestore) {
  FieldTrial* trial = new FieldTrial("Some name", 10);
  trial->AppendGroup("Winner", 10);
  std::string save_string;
  FieldTrialList::StatesToString(&save_string);
  EXPECT_EQ("Some name/Winner/", save_string);

  // It is OK if we redundantly specify a winner.
  EXPECT_TRUE(FieldTrialList::StringAugmentsState(save_string));

  // But it is an error to try to change to a different winner.
  EXPECT_FALSE(FieldTrialList::StringAugmentsState("Some name/Loser/"));
}

TEST_F(FieldTrialTest, MakeName) {
  FieldTrial* trial = new FieldTrial("Field Trial", 10);
  trial->AppendGroup("Winner", 10);
  EXPECT_EQ("Histogram_Winner",
            FieldTrial::MakeName("Histogram", "Field Trial"));
}
<|endoftext|>


<commit_before>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you 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 .
 */

#ifndef _SBSTDOBJ1_HXX
#define _SBSTDOBJ1_HXX

#include <basic/sbxobj.hxx>
#include <vcl/graph.hxx>
#include <basic/sbxfac.hxx>
#include "basicdllapi.h"

class StarBASIC;
class SbStdFactory;

//--------------------
// class SbStdFactory
//--------------------
class BASIC_DLLPUBLIC SbStdFactory : public SbxFactory
{
public:
    SbStdFactory();

    virtual SbxObject*  CreateObject( const rtl::OUString& rClassName );
};

//--------------------
// class SbStdPicture
//--------------------
class BASIC_DLLPUBLIC SbStdPicture : public SbxObject
{
protected:
    Graphic     aGraphic;

   ~SbStdPicture();
    virtual void SFX_NOTIFY( SfxBroadcaster& rBC, const TypeId& rBCType,
                             const SfxHint& rHint, const TypeId& rHintType );

    void    PropType( SbxVariable* pVar, SbxArray* pPar, sal_Bool bWrite );
    void    PropWidth( SbxVariable* pVar, SbxArray* pPar, sal_Bool bWrite );
    void    PropHeight( SbxVariable* pVar, SbxArray* pPar, sal_Bool bWrite );

public:
    TYPEINFO();

    SbStdPicture();
    virtual SbxVariable* Find( const rtl::OUString&, SbxClassType );

    Graphic GetGraphic() const { return aGraphic; }
    void    SetGraphic( const Graphic& rGrf ) { aGraphic = rGrf; }
};

//-----------------
// class SbStdFont
//-----------------
class BASIC_DLLPUBLIC SbStdFont : public SbxObject
{
protected:
    sal_Bool    bBold;
    sal_Bool    bItalic;
    sal_Bool    bStrikeThrough;
    sal_Bool    bUnderline;
    sal_uInt16  nSize;
    String  aName;

   ~SbStdFont();
    virtual void SFX_NOTIFY( SfxBroadcaster& rBC, const TypeId& rBCType,
                             const SfxHint& rHint, const TypeId& rHintType );

    void    PropBold( SbxVariable* pVar, SbxArray* pPar, sal_Bool bWrite );
    void    PropItalic( SbxVariable* pVar, SbxArray* pPar, sal_Bool bWrite );
    void    PropStrikeThrough( SbxVariable* pVar, SbxArray* pPar, sal_Bool bWrite );
    void    PropUnderline( SbxVariable* pVar, SbxArray* pPar, sal_Bool bWrite );
    void    PropSize( SbxVariable* pVar, SbxArray* pPar, sal_Bool bWrite );
    void    PropName( SbxVariable* pVar, SbxArray* pPar, sal_Bool bWrite );

public:
    TYPEINFO();

    SbStdFont();
    virtual SbxVariable* Find( const rtl::OUString&, SbxClassType );

    void    SetBold( sal_Bool bB ) { bBold = bB; }
    sal_Bool    IsBold() const { return bBold; }
    void    SetItalic( sal_Bool bI ) { bItalic = bI; }
    sal_Bool    IsItalic() const { return bItalic; }
    void    SetStrikeThrough( sal_Bool bS ) { bStrikeThrough = bS; }
    sal_Bool    IsStrikeThrough() const { return bStrikeThrough; }
    void    SetUnderline( sal_Bool bU ) { bUnderline = bU; }
    sal_Bool    IsUnderline() const { return bUnderline; }
    void    SetSize( sal_uInt16 nS ) { nSize = nS; }
    sal_uInt16  GetSize() const { return nSize; }
    void    SetFontName( const String& rName ) { aName = rName; }
    String  GetFontName() const { return aName; }
};

//----------------------
// class SbStdClipboard
//----------------------
class BASIC_DLLPUBLIC SbStdClipboard : public SbxObject
{
protected:

   ~SbStdClipboard();
    virtual void SFX_NOTIFY( SfxBroadcaster& rBC, const TypeId& rBCType,
                             const SfxHint& rHint, const TypeId& rHintType );

    void    MethClear( SbxVariable* pVar, SbxArray* pPar_, sal_Bool bWrite );
    void    MethGetData( SbxVariable* pVar, SbxArray* pPar_, sal_Bool bWrite );
    void    MethGetFormat( SbxVariable* pVar, SbxArray* pPar_, sal_Bool bWrite );
    void    MethGetText( SbxVariable* pVar, SbxArray* pPar_, sal_Bool bWrite );
    void    MethSetData( SbxVariable* pVar, SbxArray* pPar_, sal_Bool bWrite );
    void    MethSetText( SbxVariable* pVar, SbxArray* pPar_, sal_Bool bWrite );

public:
    TYPEINFO();

    SbStdClipboard();
    virtual SbxVariable* Find( const rtl::OUString&, SbxClassType );
};

#endif

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
<commit_msg>Bin useless includes<commit_after>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you 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 .
 */

#ifndef _SBSTDOBJ1_HXX
#define _SBSTDOBJ1_HXX

#include <basic/sbxobj.hxx>
#include <vcl/graph.hxx>
#include <basic/sbxfac.hxx>
#include "basicdllapi.h"

//--------------------
// class SbStdFactory
//--------------------
class BASIC_DLLPUBLIC SbStdFactory : public SbxFactory
{
public:
    SbStdFactory();

    virtual SbxObject*  CreateObject( const rtl::OUString& rClassName );
};

//--------------------
// class SbStdPicture
//--------------------
class BASIC_DLLPUBLIC SbStdPicture : public SbxObject
{
protected:
    Graphic     aGraphic;

   ~SbStdPicture();
    virtual void SFX_NOTIFY( SfxBroadcaster& rBC, const TypeId& rBCType,
                             const SfxHint& rHint, const TypeId& rHintType );

    void    PropType( SbxVariable* pVar, SbxArray* pPar, sal_Bool bWrite );
    void    PropWidth( SbxVariable* pVar, SbxArray* pPar, sal_Bool bWrite );
    void    PropHeight( SbxVariable* pVar, SbxArray* pPar, sal_Bool bWrite );

public:
    TYPEINFO();

    SbStdPicture();
    virtual SbxVariable* Find( const rtl::OUString&, SbxClassType );

    Graphic GetGraphic() const { return aGraphic; }
    void    SetGraphic( const Graphic& rGrf ) { aGraphic = rGrf; }
};

//-----------------
// class SbStdFont
//-----------------
class BASIC_DLLPUBLIC SbStdFont : public SbxObject
{
protected:
    sal_Bool    bBold;
    sal_Bool    bItalic;
    sal_Bool    bStrikeThrough;
    sal_Bool    bUnderline;
    sal_uInt16  nSize;
    String  aName;

   ~SbStdFont();
    virtual void SFX_NOTIFY( SfxBroadcaster& rBC, const TypeId& rBCType,
                             const SfxHint& rHint, const TypeId& rHintType );

    void    PropBold( SbxVariable* pVar, SbxArray* pPar, sal_Bool bWrite );
    void    PropItalic( SbxVariable* pVar, SbxArray* pPar, sal_Bool bWrite );
    void    PropStrikeThrough( SbxVariable* pVar, SbxArray* pPar, sal_Bool bWrite );
    void    PropUnderline( SbxVariable* pVar, SbxArray* pPar, sal_Bool bWrite );
    void    PropSize( SbxVariable* pVar, SbxArray* pPar, sal_Bool bWrite );
    void    PropName( SbxVariable* pVar, SbxArray* pPar, sal_Bool bWrite );

public:
    TYPEINFO();

    SbStdFont();
    virtual SbxVariable* Find( const rtl::OUString&, SbxClassType );

    void    SetBold( sal_Bool bB ) { bBold = bB; }
    sal_Bool    IsBold() const { return bBold; }
    void    SetItalic( sal_Bool bI ) { bItalic = bI; }
    sal_Bool    IsItalic() const { return bItalic; }
    void    SetStrikeThrough( sal_Bool bS ) { bStrikeThrough = bS; }
    sal_Bool    IsStrikeThrough() const { return bStrikeThrough; }
    void    SetUnderline( sal_Bool bU ) { bUnderline = bU; }
    sal_Bool    IsUnderline() const { return bUnderline; }
    void    SetSize( sal_uInt16 nS ) { nSize = nS; }
    sal_uInt16  GetSize() const { return nSize; }
    void    SetFontName( const String& rName ) { aName = rName; }
    String  GetFontName() const { return aName; }
};

//----------------------
// class SbStdClipboard
//----------------------
class BASIC_DLLPUBLIC SbStdClipboard : public SbxObject
{
protected:

   ~SbStdClipboard();
    virtual void SFX_NOTIFY( SfxBroadcaster& rBC, const TypeId& rBCType,
                             const SfxHint& rHint, const TypeId& rHintType );

    void    MethClear( SbxVariable* pVar, SbxArray* pPar_, sal_Bool bWrite );
    void    MethGetData( SbxVariable* pVar, SbxArray* pPar_, sal_Bool bWrite );
    void    MethGetFormat( SbxVariable* pVar, SbxArray* pPar_, sal_Bool bWrite );
    void    MethGetText( SbxVariable* pVar, SbxArray* pPar_, sal_Bool bWrite );
    void    MethSetData( SbxVariable* pVar, SbxArray* pPar_, sal_Bool bWrite );
    void    MethSetText( SbxVariable* pVar, SbxArray* pPar_, sal_Bool bWrite );

public:
    TYPEINFO();

    SbStdClipboard();
    virtual SbxVariable* Find( const rtl::OUString&, SbxClassType );
};

#endif

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
<|endoftext|>


<commit_before>/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/
#ifndef _BASIC_TTSTRHLP_HXX
#define _BASIC_TTSTRHLP_HXX

#include <tools/string.hxx>

#define CByteString( constAsciiStr ) ByteString( RTL_CONSTASCII_STRINGPARAM ( constAsciiStr ) )
#define CUniString( constAsciiStr ) UniString( RTL_CONSTASCII_USTRINGPARAM ( constAsciiStr ) )

#define StartKenn           CUniString("%")
#define EndKenn             CUniString("%")
#define UIdKenn             ( StartKenn.AppendAscii("UId") )
#define MethodKenn          ( StartKenn.AppendAscii("Method") )
#define TypeKenn            ( StartKenn.AppendAscii("RType") )
#define SlotKenn            ( StartKenn.AppendAscii("SlotId") )
#define RcKenn              ( StartKenn.AppendAscii("RCommand") )
#define TabKenn             ( StartKenn.AppendAscii("Tab") )
#define MakeStringParam(Type,aText) ( Type.AppendAscii("=").Append( aText ).Append( EndKenn ) )
#define MakeStringNumber(Type,nNumber)  MakeStringParam (Type, UniString::CreateFromInt32(nNumber))
#define UIdString(aID) MakeStringParam(UIdKenn,aID.GetText())
#define MethodString(nNumber) MakeStringNumber(MethodKenn,nNumber)
#define TypeString(nNumber) MakeStringNumber(TypeKenn,nNumber)
#define SlotString(nNumber) MakeStringNumber(SlotKenn,nNumber)
#define RcString(nNumber) MakeStringNumber(RcKenn,nNumber)
#define TabString(nNumber) MakeStringNumber(TabKenn,nNumber)

#define ResKenn             ( StartKenn.AppendAscii("ResId") )
#define BaseArgKenn         ( StartKenn.AppendAscii("Arg") )
#define ArgKenn(nNumber)    ( BaseArgKenn.Append( UniString::CreateFromInt32(nNumber) ) )
#define ResString(nNumber) MakeStringNumber(ResKenn,nNumber)
#define ArgString(nNumber, aText) MakeStringParam(ArgKenn(nNumber),aText)

UniString GEN_RES_STR0( ULONG nResId );
UniString GEN_RES_STR1( ULONG nResId, const String &Text1 );
UniString GEN_RES_STR2( ULONG nResId, const String &Text1, const String &Text2 );
UniString GEN_RES_STR3( ULONG nResId, const String &Text1, const String &Text2, const String &Text3 );

#define GEN_RES_STR1c( nResId, Text1 ) GEN_RES_STR1( nResId, CUniString(Text1) )
#define GEN_RES_STR2c2( nResId, Text1, Text2 ) GEN_RES_STR2( nResId, Text1, CUniString(Text2) )
#define GEN_RES_STR3c3( nResId, Text1, Text2, Text3 ) GEN_RES_STR3( nResId, Text1, Text2, CUniString(Text3) )

#define IMPL_GEN_RES_STR \
UniString GEN_RES_STR0( ULONG nResId ) { return ResString( nResId ); } \
UniString GEN_RES_STR1( ULONG nResId, const UniString &Text1 ) { return GEN_RES_STR0( nResId ).Append( ArgString( 1, Text1 ) ); } \
UniString GEN_RES_STR2( ULONG nResId, const UniString &Text1, const UniString &Text2 ) { return GEN_RES_STR1( nResId, Text1 ).Append( ArgString( 2, Text2 ) ); } \
UniString GEN_RES_STR3( ULONG nResId, const UniString &Text1, const UniString &Text2, const UniString &Text3 ) { return GEN_RES_STR2( nResId, Text1, Text2 ).Append( ArgString( 3, Text3 ) );}

#endif

<commit_msg>testtool: fix libsts to work with old testtool<commit_after>/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/
#ifndef _BASIC_TTSTRHLP_HXX
#define _BASIC_TTSTRHLP_HXX

#include <tools/string.hxx>

#define CByteString( constAsciiStr ) ByteString( RTL_CONSTASCII_STRINGPARAM ( constAsciiStr ) )
#define CUniString( constAsciiStr ) UniString( RTL_CONSTASCII_USTRINGPARAM ( constAsciiStr ) )
#define Str2Id( Str ) rtl::OUStringToOString( Str, RTL_TEXTENCODING_ASCII_US )
#define Id2Str( Id ) String( rtl::OStringToOUString( Id, RTL_TEXTENCODING_ASCII_US ) )

#define StartKenn           CUniString("%")
#define EndKenn             CUniString("%")
#define UIdKenn             ( StartKenn.AppendAscii("UId") )
#define MethodKenn          ( StartKenn.AppendAscii("Method") )
#define TypeKenn            ( StartKenn.AppendAscii("RType") )
#define SlotKenn            ( StartKenn.AppendAscii("SlotId") )
#define RcKenn              ( StartKenn.AppendAscii("RCommand") )
#define TabKenn             ( StartKenn.AppendAscii("Tab") )
#define MakeStringParam(Type,aText) ( Type.AppendAscii("=").Append( aText ).Append( EndKenn ) )
#define MakeStringNumber(Type,nNumber)  MakeStringParam (Type, UniString::CreateFromInt32(nNumber))
#define UIdString(aID) MakeStringParam(UIdKenn,String(rtl::OStringToOUString( aID, RTL_TEXTENCODING_ASCII_US )))
#define MethodString(nNumber) MakeStringNumber(MethodKenn,nNumber)
#define TypeString(nNumber) MakeStringNumber(TypeKenn,nNumber)
#define SlotString(nNumber) MakeStringNumber(SlotKenn,nNumber)
#define RcString(nNumber) MakeStringNumber(RcKenn,nNumber)
#define TabString(nNumber) MakeStringNumber(TabKenn,nNumber)

#define ResKenn             ( StartKenn.AppendAscii("ResId") )
#define BaseArgKenn         ( StartKenn.AppendAscii("Arg") )
#define ArgKenn(nNumber)    ( BaseArgKenn.Append( UniString::CreateFromInt32(nNumber) ) )
#define ResString(nNumber) MakeStringNumber(ResKenn,nNumber)
#define ArgString(nNumber, aText) MakeStringParam(ArgKenn(nNumber),aText)

UniString GEN_RES_STR0( ULONG nResId );
UniString GEN_RES_STR1( ULONG nResId, const String &Text1 );
UniString GEN_RES_STR2( ULONG nResId, const String &Text1, const String &Text2 );
UniString GEN_RES_STR3( ULONG nResId, const String &Text1, const String &Text2, const String &Text3 );

#define GEN_RES_STR1c( nResId, Text1 ) GEN_RES_STR1( nResId, CUniString(Text1) )
#define GEN_RES_STR2c2( nResId, Text1, Text2 ) GEN_RES_STR2( nResId, Text1, CUniString(Text2) )
#define GEN_RES_STR3c3( nResId, Text1, Text2, Text3 ) GEN_RES_STR3( nResId, Text1, Text2, CUniString(Text3) )

#define IMPL_GEN_RES_STR \
UniString GEN_RES_STR0( ULONG nResId ) { return ResString( nResId ); } \
UniString GEN_RES_STR1( ULONG nResId, const UniString &Text1 ) { return GEN_RES_STR0( nResId ).Append( ArgString( 1, Text1 ) ); } \
UniString GEN_RES_STR2( ULONG nResId, const UniString &Text1, const UniString &Text2 ) { return GEN_RES_STR1( nResId, Text1 ).Append( ArgString( 2, Text2 ) ); } \
UniString GEN_RES_STR3( ULONG nResId, const UniString &Text1, const UniString &Text2, const UniString &Text3 ) { return GEN_RES_STR2( nResId, Text1, Text2 ).Append( ArgString( 3, Text3 ) );}

#endif

<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  $RCSfile: process.cxx,v $
 *
 *  $Revision: 1.3 $
 *
 *  last change: $Author: hro $ $Date: 2001-05-10 15:59:46 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (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.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#ifdef WNT
#include    <tools/prewin.h>
#include "winbase.h"
#include    <tools/postwin.h>
#endif

#ifndef _ERRCODE_HXX //autogen
#include <tools/errcode.hxx>
#endif
#ifndef _VOS_PROCESS_HXX_
#include <vos/process.hxx>
#endif
#ifndef _SBXCORE_HXX
#include <svtools/sbxcore.hxx>
#endif
#ifndef _STRING_HXX
#include <tools/string.hxx>
#endif
#ifndef _OSL_FILE_HXX_
#include <osl/file.hxx>
#endif

#ifndef _BASIC_TTRESHLP_HXX
#include "ttstrhlp.hxx"
#endif

//#ifndef _BYTE_STRING_LIST
//DECLARE_LIST( ByteStringList, ByteString * );
//#define _BYTE_STRING_LIST
//#endif

#include "process.hxx"

// Konstruktor fr den Process
Process::Process()
: bWasGPF( FALSE )
, pArgumentList( NULL )
, pProcess( NULL )
, bHasBeenStarted( FALSE )
{
}

// Destruktor
Process::~Process()
{
    delete pArgumentList;
    delete pProcess;
}

BOOL Process::ImplIsRunning()
{
    if ( pProcess && bHasBeenStarted )
    {
        NAMESPACE_VOS(OProcess::TProcessInfo) aProcessInfo;
        pProcess->getInfo( NAMESPACE_VOS(OProcess::TData_ExitCode), &aProcessInfo );
        if ( !(aProcessInfo.Fields & NAMESPACE_VOS(OProcess::TData_ExitCode)) )
            return TRUE;
        else
            return FALSE;
    }
    else
        return FALSE;
}

long Process::ImplGetExitCode()
{
    if ( pProcess )
    {
        NAMESPACE_VOS(OProcess::TProcessInfo) aProcessInfo;
        pProcess->getInfo( NAMESPACE_VOS(OProcess::TData_ExitCode), &aProcessInfo );
        if ( !(aProcessInfo.Fields & NAMESPACE_VOS(OProcess::TData_ExitCode)) )
            SbxBase::SetError( SbxERR_NO_ACTIVE_OBJECT );
        return aProcessInfo.Code;
    }
    else
        SbxBase::SetError( SbxERR_NO_ACTIVE_OBJECT );
        return 0;
}

////////////////////////////////////////////////////////////////////////////

// Die Methoden:
void Process::SetImage( const String &aAppPath, const String &aAppParams )
{ // Imagedatei des Executables
    if ( pProcess && ImplIsRunning() )
        SbxBase::SetError( SbxERR_NO_ACTIVE_OBJECT );
    else
    {
        delete pArgumentList;
        delete pProcess;

        xub_StrLen i, nCount = aAppParams.GetQuotedTokenCount( CUniString("\"\"" ), ' ' );
        ::rtl::OUString *pParamList = new ::rtl::OUString[nCount];
        for ( i = 0 ; i < nCount ; i++ )
        {
            ::rtl::OUString aTemp = ::rtl::OUString(aAppParams.GetQuotedToken( i, CUniString("\"\"" ), ' ' ));
            if ( aTemp.getLength() )
                pParamList[i] = aTemp;
        }
        pArgumentList = new NAMESPACE_VOS(OArgumentList)( pParamList, nCount );
        ::rtl::OUString aNormalizedAppPath;
#ifdef TF_FILEURL
        osl::FileBase::getFileURLFromSystemPath( ::rtl::OUString(aAppPath), aNormalizedAppPath );
#else
        osl::FileBase::normalizePath( ::rtl::OUString(aAppPath), aNormalizedAppPath );
#endif
        pProcess = new NAMESPACE_VOS(OProcess)( aNormalizedAppPath );
        bHasBeenStarted = FALSE;
    }
}

BOOL Process::Start()
{ // Programm wird gestartet
    BOOL bSuccess=FALSE;
    if ( pProcess && !ImplIsRunning() )
    {
        bWasGPF = FALSE;
#ifdef WNT
//      sal_uInt32 nErrorMode = SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_NOALIGNMENTFAULTEXCEPT | SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX);
        sal_uInt32 nErrorMode = SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_NOALIGNMENTFAULTEXCEPT | SEM_NOGPFAULTERRORBOX);
        try
        {
#endif
            bSuccess = pProcess->execute( (NAMESPACE_VOS(OProcess)::TProcessOption)
                        ( NAMESPACE_VOS(OProcess)::TOption_SearchPath
                        /*| NAMESPACE_VOS(OProcess)::TOption_Detached*/
                        /*| NAMESPACE_VOS(OProcess)::TOption_Wait*/ ),
                        *pArgumentList ) == NAMESPACE_VOS(OProcess)::E_None;
#ifdef WNT
        }
        catch( ... )
        {
            bWasGPF = TRUE;
        }
        nErrorMode = SetErrorMode(nErrorMode);
#endif
        bHasBeenStarted = bSuccess;
    }
    else
        SbxBase::SetError( SbxERR_NO_ACTIVE_OBJECT );
    return bSuccess;
}

ULONG Process::GetExitCode()
{ // ExitCode des Programms(nachdem es beendet ist)
    return ImplGetExitCode();
}

BOOL Process::IsRunning()
{ // Programm luft noch
    return ImplIsRunning();
}

BOOL Process::WasGPF()
{ // Programm mit GPF o.. abgebrochen
#ifdef WNT
    return ImplGetExitCode() == 3221225477;
#else
    return bWasGPF;
#endif
}

<commit_msg>#90267#<commit_after>/*************************************************************************
 *
 *  $RCSfile: process.cxx,v $
 *
 *  $Revision: 1.4 $
 *
 *  last change: $Author: gh $ $Date: 2001-07-30 12:14:09 $
 *
 *  The Contents of this file are made available subject to the terms of
 *  either of the following licenses
 *
 *         - GNU Lesser General Public License Version 2.1
 *         - Sun Industry Standards Source License Version 1.1
 *
 *  Sun Microsystems Inc., October, 2000
 *
 *  GNU Lesser General Public License Version 2.1
 *  =============================================
 *  Copyright 2000 by Sun Microsystems, Inc.
 *  901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License version 2.1, as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *  MA  02111-1307  USA
 *
 *
 *  Sun Industry Standards Source License Version 1.1
 *  =================================================
 *  The contents of this file are subject to the Sun Industry Standards
 *  Source License Version 1.1 (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.openoffice.org/license.html.
 *
 *  Software provided under this License is provided on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
 *  WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
 *  MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
 *  See the License for the specific provisions governing your rights and
 *  obligations concerning the Software.
 *
 *  The Initial Developer of the Original Code is: Sun Microsystems, Inc.
 *
 *  Copyright: 2000 by Sun Microsystems, Inc.
 *
 *  All Rights Reserved.
 *
 *  Contributor(s): _______________________________________
 *
 *
 ************************************************************************/

#ifdef WNT
#include    <tools/prewin.h>
#include "winbase.h"
#include    <tools/postwin.h>
#endif

#ifndef _ERRCODE_HXX //autogen
#include <tools/errcode.hxx>
#endif
#ifndef _VOS_PROCESS_HXX_
#include <vos/process.hxx>
#endif
#ifndef _SBXCORE_HXX
#include <svtools/sbxcore.hxx>
#endif
#ifndef _STRING_HXX
#include <tools/string.hxx>
#endif
#ifndef _OSL_FILE_HXX_
#include <osl/file.hxx>
#endif

#ifndef _BASIC_TTRESHLP_HXX
#include "ttstrhlp.hxx"
#endif

//#ifndef _BYTE_STRING_LIST
//DECLARE_LIST( ByteStringList, ByteString * );
//#define _BYTE_STRING_LIST
//#endif

#include "process.hxx"

// Konstruktor fr den Process
Process::Process()
: bWasGPF( FALSE )
, pArgumentList( NULL )
, pProcess( NULL )
, bHasBeenStarted( FALSE )
{
}

// Destruktor
Process::~Process()
{
    delete pArgumentList;
    delete pProcess;
}

BOOL Process::ImplIsRunning()
{
    if ( pProcess && bHasBeenStarted )
    {
        NAMESPACE_VOS(OProcess::TProcessInfo) aProcessInfo;
        pProcess->getInfo( NAMESPACE_VOS(OProcess::TData_ExitCode), &aProcessInfo );
        if ( !(aProcessInfo.Fields & NAMESPACE_VOS(OProcess::TData_ExitCode)) )
            return TRUE;
        else
            return FALSE;
    }
    else
        return FALSE;
}

long Process::ImplGetExitCode()
{
    if ( pProcess )
    {
        NAMESPACE_VOS(OProcess::TProcessInfo) aProcessInfo;
        pProcess->getInfo( NAMESPACE_VOS(OProcess::TData_ExitCode), &aProcessInfo );
        if ( !(aProcessInfo.Fields & NAMESPACE_VOS(OProcess::TData_ExitCode)) )
            SbxBase::SetError( SbxERR_NO_ACTIVE_OBJECT );
        return aProcessInfo.Code;
    }
    else
        SbxBase::SetError( SbxERR_NO_ACTIVE_OBJECT );
        return 0;
}

////////////////////////////////////////////////////////////////////////////

// Die Methoden:
void Process::SetImage( const String &aAppPath, const String &aAppParams )
{ // Imagedatei des Executables
    if ( pProcess && ImplIsRunning() )
        SbxBase::SetError( SbxERR_NO_ACTIVE_OBJECT );
    else
    {
        delete pArgumentList;
        delete pProcess;

        xub_StrLen i, nCount = aAppParams.GetQuotedTokenCount( CUniString("\"\"" ), ' ' );
        ::rtl::OUString *pParamList = new ::rtl::OUString[nCount];
        for ( i = 0 ; i < nCount ; i++ )
        {
            ::rtl::OUString aTemp = ::rtl::OUString(aAppParams.GetQuotedToken( i, CUniString("\"\"" ), ' ' ));
            if ( aTemp.getLength() )
                pParamList[i] = aTemp;
        }
        pArgumentList = new NAMESPACE_VOS(OArgumentList)( pParamList, nCount );
        ::rtl::OUString aNormalizedAppPath;
        osl::FileBase::getFileURLFromSystemPath( ::rtl::OUString(aAppPath), aNormalizedAppPath );
        pProcess = new NAMESPACE_VOS(OProcess)( aNormalizedAppPath );
        bHasBeenStarted = FALSE;
    }
}

BOOL Process::Start()
{ // Programm wird gestartet
    BOOL bSuccess=FALSE;
    if ( pProcess && !ImplIsRunning() )
    {
        bWasGPF = FALSE;
#ifdef WNT
//      sal_uInt32 nErrorMode = SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_NOALIGNMENTFAULTEXCEPT | SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX);
        sal_uInt32 nErrorMode = SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_NOALIGNMENTFAULTEXCEPT | SEM_NOGPFAULTERRORBOX);
        try
        {
#endif
            bSuccess = pProcess->execute( (NAMESPACE_VOS(OProcess)::TProcessOption)
                        ( NAMESPACE_VOS(OProcess)::TOption_SearchPath
                        /*| NAMESPACE_VOS(OProcess)::TOption_Detached*/
                        /*| NAMESPACE_VOS(OProcess)::TOption_Wait*/ ),
                        *pArgumentList ) == NAMESPACE_VOS(OProcess)::E_None;
#ifdef WNT
        }
        catch( ... )
        {
            bWasGPF = TRUE;
        }
        nErrorMode = SetErrorMode(nErrorMode);
#endif
        bHasBeenStarted = bSuccess;
    }
    else
        SbxBase::SetError( SbxERR_NO_ACTIVE_OBJECT );
    return bSuccess;
}

ULONG Process::GetExitCode()
{ // ExitCode des Programms(nachdem es beendet ist)
    return ImplGetExitCode();
}

BOOL Process::IsRunning()
{ // Programm luft noch
    return ImplIsRunning();
}

BOOL Process::WasGPF()
{ // Programm mit GPF o.. abgebrochen
#ifdef WNT
    return ImplGetExitCode() == 3221225477;
#else
    return bWasGPF;
#endif
}

<|endoftext|>