<commit_before>/*
 * Copyright (C) 2015 Cloudius Systems, Ltd.
 */

#include <seastar/core/thread.hh>
#include "core/do_with.hh"
#include "cql_test_env.hh"
#include "cql3/query_processor.hh"
#include "cql3/query_options.hh"
#include "core/distributed.hh"
#include "core/shared_ptr.hh"
#include "utils/UUID_gen.hh"
#include "service/migration_manager.hh"
#include "message/messaging_service.hh"
#include "service/storage_service.hh"
#include "db/config.hh"
#include "db/batchlog_manager.hh"
#include "schema_builder.hh"
#include "init.hh"

class in_memory_cql_env : public cql_test_env {
public:
    static auto constexpr ks_name = "ks";
private:
    ::shared_ptr<distributed<database>> _db;
    ::shared_ptr<distributed<cql3::query_processor>> _qp;
private:
    struct core_local_state {
        service::client_state client_state;

        core_local_state()
            : client_state(service::client_state::for_external_calls()) {
            client_state.set_keyspace(ks_name);
        }

        future<> stop() {
            return make_ready_future<>();
        }
    };
    distributed<core_local_state> _core_local;
private:
    auto make_query_state() {
        return ::make_shared<service::query_state>(_core_local.local().client_state);
    }
public:
    in_memory_cql_env(
        ::shared_ptr<distributed<database>> db,
        ::shared_ptr<distributed<cql3::query_processor>> qp)
            : _db(db)
            , _qp(qp)
    { }

    virtual future<::shared_ptr<transport::messages::result_message>> execute_cql(const sstring& text) override {
        auto qs = make_query_state();
        return _qp->local().process(text, *qs, cql3::query_options::DEFAULT).finally([qs] {});
    }

    virtual future<::shared_ptr<transport::messages::result_message>> execute_cql(
        const sstring& text,
        std::unique_ptr<cql3::query_options> qo) override
    {
        auto qs = make_query_state();
        auto& lqo = *qo;
        return _qp->local().process(text, *qs, lqo).finally([qs, qo = std::move(qo)] {});
    }

    virtual future<bytes> prepare(sstring query) override {
        return _qp->invoke_on_all([query, this] (auto& local_qp) {
            auto qs = this->make_query_state();
            return local_qp.prepare(query, *qs).finally([qs] {}).discard_result();
        }).then([query, this] {
            return _qp->local().compute_id(query, ks_name);
        });
    }

    virtual future<::shared_ptr<transport::messages::result_message>> execute_prepared(
        bytes id,
        std::vector<bytes_opt> values) override
    {
        auto prepared = _qp->local().get_prepared(id);
        assert(bool(prepared));
        auto stmt = prepared->statement;
        assert(stmt->get_bound_terms() == values.size());

        int32_t protocol_version = 3;
        auto options = ::make_shared<cql3::query_options>(db::consistency_level::ONE, std::experimental::nullopt, std::move(values), std::move(std::vector<bytes_view_opt>{}), false,
                                                          cql3::query_options::specific_options::DEFAULT, protocol_version, serialization_format::use_32_bit());
        options->prepare(prepared->bound_names);

        auto qs = make_query_state();
        return _qp->local().process_statement(stmt, *qs, *options)
            .finally([options, qs] {});
    }

    virtual future<> create_table(std::function<schema(const sstring&)> schema_maker) override {
        auto id = utils::UUID_gen::get_time_UUID();
        return _db->invoke_on_all([schema_maker, id, this] (database& db) {
            schema_builder builder(make_lw_shared(schema_maker(ks_name)));
            builder.set_uuid(id);
            auto cf_schema = builder.build(schema_builder::compact_storage::no);
            auto& ks = db.find_keyspace(ks_name);
            auto cfg = ks.make_column_family_config(*cf_schema);
            db.add_column_family(std::move(cf_schema), std::move(cfg));
        });
    }

    virtual future<> require_keyspace_exists(const sstring& ks_name) override {
        auto& db = _db->local();
        assert(db.has_keyspace(ks_name));
        return make_ready_future<>();
    }

    virtual future<> require_table_exists(const sstring& ks_name, const sstring& table_name) override {
        auto& db = _db->local();
        assert(db.has_schema(ks_name, table_name));
        return make_ready_future<>();
    }

    virtual future<> require_column_has_value(const sstring& table_name,
                                      std::vector<boost::any> pk,
                                      std::vector<boost::any> ck,
                                      const sstring& column_name,
                                      boost::any expected) override {
        auto& db = _db->local();
        auto& cf = db.find_column_family(ks_name, table_name);
        auto schema = cf.schema();
        auto pkey = partition_key::from_deeply_exploded(*schema, pk);
        auto dk = dht::global_partitioner().decorate_key(*schema, pkey);
        auto shard = db.shard_of(dk._token);
        return _db->invoke_on(shard, [pkey = std::move(pkey),
                                      ck = std::move(ck),
                                      ks_name = std::move(ks_name),
                                      column_name = std::move(column_name),
                                      expected = std::move(expected),
                                      table_name = std::move(table_name)] (database& db) mutable {
          auto& cf = db.find_column_family(ks_name, table_name);
          auto schema = cf.schema();
          return cf.find_partition_slow(pkey).then([schema, ck, column_name, expected] (column_family::const_mutation_partition_ptr p) {
            assert(p != nullptr);
            auto row = p->find_row(clustering_key::from_deeply_exploded(*schema, ck));
            assert(row != nullptr);
            auto col_def = schema->get_column_definition(utf8_type->decompose(column_name));
            assert(col_def != nullptr);
            const atomic_cell_or_collection* cell = row->find_cell(col_def->id);
            if (!cell) {
                assert(((void)"column not set", 0));
            }
            bytes actual;
            if (!col_def->type->is_multi_cell()) {
                auto c = cell->as_atomic_cell();
                assert(c.is_live());
                actual = { c.value().begin(), c.value().end() };
            } else {
                auto c = cell->as_collection_mutation();
                auto type = dynamic_pointer_cast<const collection_type_impl>(col_def->type);
                actual = type->to_value(type->deserialize_mutation_form(c),
                                        serialization_format::internal());
            }
            assert(col_def->type->equal(actual, col_def->type->decompose(expected)));
          });
        });
    }

    virtual database& local_db() override {
        return _db->local();
    }

    cql3::query_processor& local_qp() override {
        return _qp->local();
    }

    future<> start() {
        return _core_local.start().then([this] () {
            auto query = sprint("create keyspace %s with replication = { 'class' : 'org.apache.cassandra.locator.SimpleStrategy', 'replication_factor' : 1 };", sstring{ks_name});
            return execute_cql(query).discard_result().then([] {
                return make_ready_future<>();
            });
        });
    }

    virtual future<> stop() override {
        return _core_local.stop().then([this] {
            return db::get_batchlog_manager().stop().then([this] {
                return _qp->stop().then([this] {
                    return service::get_migration_manager().stop().then([this] {
                        return service::get_storage_proxy().stop().then([this] {
                            return _db->stop().then([this] {
                                return locator::i_endpoint_snitch::stop_snitch();
                            });
                        });
                    });
                });
            });
        });
    }
};

future<> init_once(shared_ptr<distributed<database>> db) {
    static bool done = false;
    if (!done) {
        done = true;
        // FIXME: we leak db, since we're initializing the global storage_service with it.
        new shared_ptr<distributed<database>>(db);
        return init_storage_service(*db).then([] {
            return init_ms_fd_gossiper("127.0.0.1", db::config::seed_provider_type());
        });
    } else {
        return make_ready_future();
    }
}

future<::shared_ptr<cql_test_env>> make_env_for_test() {
    return locator::i_endpoint_snitch::create_snitch("SimpleSnitch").then([] {
        auto db = ::make_shared<distributed<database>>();
        return init_once(db).then([db] {
            return seastar::async([db] {
                auto cfg = make_lw_shared<db::config>();
                cfg->data_file_directories() = {};
                cfg->volatile_system_keyspace_for_testing() = true;
                db->start(std::move(*cfg)).get();

                distributed<service::storage_proxy>& proxy = service::get_storage_proxy();
                distributed<service::migration_manager>& mm = service::get_migration_manager();
                distributed<db::batchlog_manager>& bm = db::get_batchlog_manager();

                auto qp = ::make_shared<distributed<cql3::query_processor>>();
                proxy.start(std::ref(*db)).get();
                mm.start().get();
                qp->start(std::ref(proxy), std::ref(*db)).get();

                db::system_keyspace::init_local_cache().get();

                auto& ss = service::get_local_storage_service();
                static bool storage_service_started = false;
                if (!storage_service_started) {
                    storage_service_started = true;
                    ss.init_server().get();
                }

                bm.start(std::ref(*qp));

                auto env = ::make_shared<in_memory_cql_env>(db, qp);
                env->start().get();

                return dynamic_pointer_cast<cql_test_env>(env);
            });
        });
    });
}

future<> do_with_cql_env(std::function<future<>(cql_test_env&)> func) {
    return make_env_for_test().then([func = std::move(func)] (auto e) mutable {
        return do_with(std::move(func), [e] (auto& f) {
            return f(*e);
        }).finally([e] {
            return e->stop().finally([e] {});
        });
    });
}
<commit_msg>tests/cql_env: make sure that value views are correct<commit_after>/*
 * Copyright (C) 2015 Cloudius Systems, Ltd.
 */

#include <seastar/core/thread.hh>
#include "core/do_with.hh"
#include "cql_test_env.hh"
#include "cql3/query_processor.hh"
#include "cql3/query_options.hh"
#include "core/distributed.hh"
#include "core/shared_ptr.hh"
#include "utils/UUID_gen.hh"
#include "service/migration_manager.hh"
#include "message/messaging_service.hh"
#include "service/storage_service.hh"
#include "db/config.hh"
#include "db/batchlog_manager.hh"
#include "schema_builder.hh"
#include "init.hh"

class in_memory_cql_env : public cql_test_env {
public:
    static auto constexpr ks_name = "ks";
private:
    ::shared_ptr<distributed<database>> _db;
    ::shared_ptr<distributed<cql3::query_processor>> _qp;
private:
    struct core_local_state {
        service::client_state client_state;

        core_local_state()
            : client_state(service::client_state::for_external_calls()) {
            client_state.set_keyspace(ks_name);
        }

        future<> stop() {
            return make_ready_future<>();
        }
    };
    distributed<core_local_state> _core_local;
private:
    auto make_query_state() {
        return ::make_shared<service::query_state>(_core_local.local().client_state);
    }
public:
    in_memory_cql_env(
        ::shared_ptr<distributed<database>> db,
        ::shared_ptr<distributed<cql3::query_processor>> qp)
            : _db(db)
            , _qp(qp)
    { }

    virtual future<::shared_ptr<transport::messages::result_message>> execute_cql(const sstring& text) override {
        auto qs = make_query_state();
        return _qp->local().process(text, *qs, cql3::query_options::DEFAULT).finally([qs] {});
    }

    virtual future<::shared_ptr<transport::messages::result_message>> execute_cql(
        const sstring& text,
        std::unique_ptr<cql3::query_options> qo) override
    {
        auto qs = make_query_state();
        auto& lqo = *qo;
        return _qp->local().process(text, *qs, lqo).finally([qs, qo = std::move(qo)] {});
    }

    virtual future<bytes> prepare(sstring query) override {
        return _qp->invoke_on_all([query, this] (auto& local_qp) {
            auto qs = this->make_query_state();
            return local_qp.prepare(query, *qs).finally([qs] {}).discard_result();
        }).then([query, this] {
            return _qp->local().compute_id(query, ks_name);
        });
    }

    virtual future<::shared_ptr<transport::messages::result_message>> execute_prepared(
        bytes id,
        std::vector<bytes_opt> values) override
    {
        auto prepared = _qp->local().get_prepared(id);
        assert(bool(prepared));
        auto stmt = prepared->statement;
        assert(stmt->get_bound_terms() == values.size());

        auto options = ::make_shared<cql3::query_options>(std::move(values));
        options->prepare(prepared->bound_names);

        auto qs = make_query_state();
        return _qp->local().process_statement(stmt, *qs, *options)
            .finally([options, qs] {});
    }

    virtual future<> create_table(std::function<schema(const sstring&)> schema_maker) override {
        auto id = utils::UUID_gen::get_time_UUID();
        return _db->invoke_on_all([schema_maker, id, this] (database& db) {
            schema_builder builder(make_lw_shared(schema_maker(ks_name)));
            builder.set_uuid(id);
            auto cf_schema = builder.build(schema_builder::compact_storage::no);
            auto& ks = db.find_keyspace(ks_name);
            auto cfg = ks.make_column_family_config(*cf_schema);
            db.add_column_family(std::move(cf_schema), std::move(cfg));
        });
    }

    virtual future<> require_keyspace_exists(const sstring& ks_name) override {
        auto& db = _db->local();
        assert(db.has_keyspace(ks_name));
        return make_ready_future<>();
    }

    virtual future<> require_table_exists(const sstring& ks_name, const sstring& table_name) override {
        auto& db = _db->local();
        assert(db.has_schema(ks_name, table_name));
        return make_ready_future<>();
    }

    virtual future<> require_column_has_value(const sstring& table_name,
                                      std::vector<boost::any> pk,
                                      std::vector<boost::any> ck,
                                      const sstring& column_name,
                                      boost::any expected) override {
        auto& db = _db->local();
        auto& cf = db.find_column_family(ks_name, table_name);
        auto schema = cf.schema();
        auto pkey = partition_key::from_deeply_exploded(*schema, pk);
        auto dk = dht::global_partitioner().decorate_key(*schema, pkey);
        auto shard = db.shard_of(dk._token);
        return _db->invoke_on(shard, [pkey = std::move(pkey),
                                      ck = std::move(ck),
                                      ks_name = std::move(ks_name),
                                      column_name = std::move(column_name),
                                      expected = std::move(expected),
                                      table_name = std::move(table_name)] (database& db) mutable {
          auto& cf = db.find_column_family(ks_name, table_name);
          auto schema = cf.schema();
          return cf.find_partition_slow(pkey).then([schema, ck, column_name, expected] (column_family::const_mutation_partition_ptr p) {
            assert(p != nullptr);
            auto row = p->find_row(clustering_key::from_deeply_exploded(*schema, ck));
            assert(row != nullptr);
            auto col_def = schema->get_column_definition(utf8_type->decompose(column_name));
            assert(col_def != nullptr);
            const atomic_cell_or_collection* cell = row->find_cell(col_def->id);
            if (!cell) {
                assert(((void)"column not set", 0));
            }
            bytes actual;
            if (!col_def->type->is_multi_cell()) {
                auto c = cell->as_atomic_cell();
                assert(c.is_live());
                actual = { c.value().begin(), c.value().end() };
            } else {
                auto c = cell->as_collection_mutation();
                auto type = dynamic_pointer_cast<const collection_type_impl>(col_def->type);
                actual = type->to_value(type->deserialize_mutation_form(c),
                                        serialization_format::internal());
            }
            assert(col_def->type->equal(actual, col_def->type->decompose(expected)));
          });
        });
    }

    virtual database& local_db() override {
        return _db->local();
    }

    cql3::query_processor& local_qp() override {
        return _qp->local();
    }

    future<> start() {
        return _core_local.start().then([this] () {
            auto query = sprint("create keyspace %s with replication = { 'class' : 'org.apache.cassandra.locator.SimpleStrategy', 'replication_factor' : 1 };", sstring{ks_name});
            return execute_cql(query).discard_result().then([] {
                return make_ready_future<>();
            });
        });
    }

    virtual future<> stop() override {
        return _core_local.stop().then([this] {
            return db::get_batchlog_manager().stop().then([this] {
                return _qp->stop().then([this] {
                    return service::get_migration_manager().stop().then([this] {
                        return service::get_storage_proxy().stop().then([this] {
                            return _db->stop().then([this] {
                                return locator::i_endpoint_snitch::stop_snitch();
                            });
                        });
                    });
                });
            });
        });
    }
};

future<> init_once(shared_ptr<distributed<database>> db) {
    static bool done = false;
    if (!done) {
        done = true;
        // FIXME: we leak db, since we're initializing the global storage_service with it.
        new shared_ptr<distributed<database>>(db);
        return init_storage_service(*db).then([] {
            return init_ms_fd_gossiper("127.0.0.1", db::config::seed_provider_type());
        });
    } else {
        return make_ready_future();
    }
}

future<::shared_ptr<cql_test_env>> make_env_for_test() {
    return locator::i_endpoint_snitch::create_snitch("SimpleSnitch").then([] {
        auto db = ::make_shared<distributed<database>>();
        return init_once(db).then([db] {
            return seastar::async([db] {
                auto cfg = make_lw_shared<db::config>();
                cfg->data_file_directories() = {};
                cfg->volatile_system_keyspace_for_testing() = true;
                db->start(std::move(*cfg)).get();

                distributed<service::storage_proxy>& proxy = service::get_storage_proxy();
                distributed<service::migration_manager>& mm = service::get_migration_manager();
                distributed<db::batchlog_manager>& bm = db::get_batchlog_manager();

                auto qp = ::make_shared<distributed<cql3::query_processor>>();
                proxy.start(std::ref(*db)).get();
                mm.start().get();
                qp->start(std::ref(proxy), std::ref(*db)).get();

                db::system_keyspace::init_local_cache().get();

                auto& ss = service::get_local_storage_service();
                static bool storage_service_started = false;
                if (!storage_service_started) {
                    storage_service_started = true;
                    ss.init_server().get();
                }

                bm.start(std::ref(*qp));

                auto env = ::make_shared<in_memory_cql_env>(db, qp);
                env->start().get();

                return dynamic_pointer_cast<cql_test_env>(env);
            });
        });
    });
}

future<> do_with_cql_env(std::function<future<>(cql_test_env&)> func) {
    return make_env_for_test().then([func = std::move(func)] (auto e) mutable {
        return do_with(std::move(func), [e] (auto& f) {
            return f(*e);
        }).finally([e] {
            return e->stop().finally([e] {});
        });
    });
}
<|endoftext|>


<commit_before>#ifndef CMDSTAN_WRITE_PROFILING_HPP
#define CMDSTAN_WRITE_PROFILING_HPP

#include <stan/math/rev/core/profiling.hpp>
#include <stan/callbacks/writer.hpp>
#include <stan/version.hpp>
#include <string>
#include <vector>

namespace cmdstan {
void write_profiling(stan::callbacks::writer& writer,
                     stan::math::profile_map& p) {
    stan::math::profile_map::iterator it;
    std::stringstream profile_csv_stream;
    profile_csv_stream << "name,thread_id,time_total,forward_time,reverse_time,chain_stack_total,nochain_stack_total,autodiff_passes,no_autodiff_passes" << std::endl;
    for (it = p.begin(); it != p.end(); it++) {
        profile_csv_stream
            // name
            <<  it->first.first << ","
            // thread_id
            << it->first.second << ","  //
            // time_total
            << (it->second.get_fwd_time() + it->second.get_rev_time()) << ","
            // forward_time
            << it->second.get_fwd_time() << ","
            // reverse_time
            << it->second.get_rev_time() << ","
            // chain_stack_total
            << it->second.get_chain_stack_used() << ","
            // nochain_stack_total
            << it->second.get_nochain_stack_used() << ","
            // autodiff_passes
            << it->second.get_num_rev_passes()  << ","
            // no_autodiff_passes
            << it->second.get_num_no_AD_fwd_passes()
            << std::endl;
    }
    writer(profile_csv_stream.str());
}
}  // namespace cmdstan
#endif
<commit_msg>fix names<commit_after>#ifndef CMDSTAN_WRITE_PROFILING_HPP
#define CMDSTAN_WRITE_PROFILING_HPP

#include <stan/math/rev/core/profiling.hpp>
#include <stan/callbacks/writer.hpp>
#include <stan/version.hpp>
#include <string>
#include <vector>

namespace cmdstan {
void write_profiling(stan::callbacks::writer& writer,
                     stan::math::profile_map& p) {
    stan::math::profile_map::iterator it;
    std::stringstream profile_csv_stream;
    profile_csv_stream << "name,thread_id,time,forward_time,reverse_time,chain_stack,no_chain_stack,autodiff_calls,no_autodiff_calls" << std::endl;
    for (it = p.begin(); it != p.end(); it++) {
        profile_csv_stream
            // name
            <<  it->first.first << ","
            // thread_id
            << it->first.second << ","  //
            // time_total
            << (it->second.get_fwd_time() + it->second.get_rev_time()) << ","
            // forward_time
            << it->second.get_fwd_time() << ","
            // reverse_time
            << it->second.get_rev_time() << ","
            // chain_stack_total
            << it->second.get_chain_stack_used() << ","
            // nochain_stack_total
            << it->second.get_nochain_stack_used() << ","
            // autodiff_passes
            << it->second.get_num_rev_passes()  << ","
            // no_autodiff_passes
            << it->second.get_num_no_AD_fwd_passes()
            << std::endl;
    }
    writer(profile_csv_stream.str());
}
}  // namespace cmdstan
#endif
<|endoftext|>


<commit_before>#ifndef CMDSTAN_WRITE_PROFILING_HPP
#define CMDSTAN_WRITE_PROFILING_HPP

#include <stan/math/rev/core/profiling.hpp>
#include <stan/callbacks/writer.hpp>
#include <stan/version.hpp>
#include <string>
#include <vector>

namespace cmdstan {

/**
 * Writes the data from the map of profiles in a CSV format
 * to the supplied writer.
 *
 * @param writer object of a Stan writer class to write to.
 * @param p reference to the map of profiles
 */
void write_profiling(stan::callbacks::writer& writer,
                     stan::math::profile_map& p) {
  stan::math::profile_map::iterator it;
  std::stringstream profile_csv_stream;
  profile_csv_stream << "name,thread_id,time,forward_time,reverse_time,chain_"
                        "stack,no_chain_stack,autodiff_calls,no_autodiff_calls"
                     << std::endl;
  for (it = p.begin(); it != p.end(); it++) {
    profile_csv_stream
        << it->first.first
        << ","
        << it->first.second
        << ","
        << (it->second.get_fwd_time() + it->second.get_rev_time())
        << ","
        << it->second.get_fwd_time()
        << ","
        << it->second.get_rev_time()
        << ","
        << it->second.get_chain_stack_used()
        << ","
        << it->second.get_nochain_stack_used()
        << ","
        << it->second.get_num_rev_passes()
        << ","
        << it->second.get_num_no_AD_fwd_passes() << std::endl;
  }
  writer(profile_csv_stream.str());
}
}  // namespace cmdstan
#endif
<commit_msg>[Jenkins] auto-formatting by clang-format version 6.0.0-1ubuntu2~16.04.1 (tags/RELEASE_600/final)<commit_after>#ifndef CMDSTAN_WRITE_PROFILING_HPP
#define CMDSTAN_WRITE_PROFILING_HPP

#include <stan/math/rev/core/profiling.hpp>
#include <stan/callbacks/writer.hpp>
#include <stan/version.hpp>
#include <string>
#include <vector>

namespace cmdstan {

/**
 * Writes the data from the map of profiles in a CSV format
 * to the supplied writer.
 *
 * @param writer object of a Stan writer class to write to.
 * @param p reference to the map of profiles
 */
void write_profiling(stan::callbacks::writer& writer,
                     stan::math::profile_map& p) {
  stan::math::profile_map::iterator it;
  std::stringstream profile_csv_stream;
  profile_csv_stream << "name,thread_id,time,forward_time,reverse_time,chain_"
                        "stack,no_chain_stack,autodiff_calls,no_autodiff_calls"
                     << std::endl;
  for (it = p.begin(); it != p.end(); it++) {
    profile_csv_stream << it->first.first << "," << it->first.second << ","
                       << (it->second.get_fwd_time()
                           + it->second.get_rev_time())
                       << "," << it->second.get_fwd_time() << ","
                       << it->second.get_rev_time() << ","
                       << it->second.get_chain_stack_used() << ","
                       << it->second.get_nochain_stack_used() << ","
                       << it->second.get_num_rev_passes() << ","
                       << it->second.get_num_no_AD_fwd_passes() << std::endl;
  }
  writer(profile_csv_stream.str());
}
}  // namespace cmdstan
#endif
<|endoftext|>


<commit_before>#ifndef VEXCL_UTIL_HPP
#define VEXCL_UTIL_HPP

/*
The MIT License

Copyright (c) 2012 Denis Demidov <ddemidov@ksu.ru>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

/**
 * \file   util.hpp
 * \author Denis Demidov <ddemidov@ksu.ru>
 * \brief  OpenCL general utilities.
 */

#ifdef WIN32
#  pragma warning(push)
#  pragma warning(disable : 4267 4290)
#  define NOMINMAX
#endif

#ifndef __CL_ENABLE_EXCEPTIONS
#  define __CL_ENABLE_EXCEPTIONS
#endif

#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <map>
#include <algorithm>
#include <type_traits>
#include <functional>
#include <CL/cl.hpp>
#include <climits>

typedef unsigned int  uint;
typedef unsigned char uchar;

namespace vex {

/// Convert typename to string.
template <class T> inline std::string type_name()  { return "undefined_type"; }
template <> inline std::string type_name<float>()  { return "float"; }
template <> inline std::string type_name<double>() { return "double"; }
template <> inline std::string type_name<int>()    { return "int"; }
template <> inline std::string type_name<char>()   { return "char"; }
template <> inline std::string type_name<bool>()   { return "bool"; }
template <> inline std::string type_name<uint>()   { return "unsigned int"; }
template <> inline std::string type_name<uchar>()  { return "unsigned char"; }

#if (__WORDSIZE == 64) || defined(_WIN64)
template <> inline std::string type_name<size_t>()    { return "ulong"; }
template <> inline std::string type_name<ptrdiff_t>() { return "long"; }
#endif

const std::string standard_kernel_header = std::string(
	"#if defined(cl_khr_fp64)\n"
	"#  pragma OPENCL EXTENSION cl_khr_fp64: enable\n"
	"#elif defined(cl_amd_fp64)\n"
	"#  pragma OPENCL EXTENSION cl_amd_fp64: enable\n"
	"#endif\n"
	);

/// \cond INTERNAL

/// Binary operations with their traits.
namespace binop {
    enum kind {
	Add,
	Subtract,
	Multiply,
	Divide,
	Remainder,
	Greater,
	Less,
	GreaterEqual,
	LessEqual,
	Equal,
	NotEqual,
	BitwiseAnd,
	BitwiseOr,
	BitwiseXor,
	LogicalAnd,
	LogicalOr,
	RightShift,
	LeftShift
    };

    template <kind> struct traits {};

#define BOP_TRAITS(kind, op, nm)   \
    template <> struct traits<kind> {  \
	static std::string oper() { return op; } \
	static std::string name() { return nm; } \
    };

    BOP_TRAITS(Add,          "+",  "Add_")
    BOP_TRAITS(Subtract,     "-",  "Sub_")
    BOP_TRAITS(Multiply,     "*",  "Mul_")
    BOP_TRAITS(Divide,       "/",  "Div_")
    BOP_TRAITS(Remainder,    "%",  "Mod_")
    BOP_TRAITS(Greater,      ">",  "Gtr_")
    BOP_TRAITS(Less,         "<",  "Lss_")
    BOP_TRAITS(GreaterEqual, ">=", "Geq_")
    BOP_TRAITS(LessEqual,    "<=", "Leq_")
    BOP_TRAITS(Equal,        "==", "Equ_")
    BOP_TRAITS(NotEqual,     "!=", "Neq_")
    BOP_TRAITS(BitwiseAnd,   "&",  "BAnd_")
    BOP_TRAITS(BitwiseOr,    "|",  "BOr_")
    BOP_TRAITS(BitwiseXor,   "^",  "BXor_")
    BOP_TRAITS(LogicalAnd,   "&&", "LAnd_")
    BOP_TRAITS(LogicalOr,    "||", "LOr_")
    BOP_TRAITS(RightShift,   ">>", "Rsh_")
    BOP_TRAITS(LeftShift,    "<<", "Lsh_")

#undef BOP_TRAITS
}

/// Return next power of 2.
inline size_t nextpow2(size_t x) {
    --x;
    x |= x >> 1U;
    x |= x >> 2U;
    x |= x >> 4U;
    x |= x >> 8U;
    x |= x >> 16U;
    return ++x;
}

/// Align n to the next multiple of m.
inline size_t alignup(size_t n, size_t m = 16U) {
    return n % m ? n - n % m + m : n;
}

/// Weights device wrt to vector performance.
/**
 * Launches the following kernel on each device:
 * \code
 * a = b + c;
 * \endcode
 * where a, b and c are device vectors. Each device gets portion of the vector
 * proportional to the performance of this operation.
 */
inline double device_vector_perf(
	const cl::Context &context, const cl::Device &device
	);

/// Weights device wrt to spmv performance.
/**
 * Launches the following kernel on each device:
 * \code
 * y = A * x;
 * \endcode
 * where x and y are vectors, and A is matrix for 3D Poisson problem in square
 * domain. Each device gets portion of the vector proportional to the
 * performance of this operation.
 */
inline double device_spmv_perf(
	const cl::Context &context, const cl::Device &device
	);

/// Assigns equal weight to each device.
/**
 * This results in equal partitioning.
 */
inline double equal_weights(
	const cl::Context &context, const cl::Device &device
	)
{
    return 1;
}

/// Partitioning scheme for vectors and matrices.
/**
 * Should be set once before any object of vector or matrix type is declared.
 * Otherwise default parttioning function (partition_by_vector_perf) is
 * selected.
 */
template <bool dummy = true>
struct partitioning_scheme {
    typedef std::function<
	double(const cl::Context&, const cl::Device&)
	> weight_function;

    static void set(weight_function f) {
	if (!is_set) {
	    weight = f;
	    is_set = true;
	} else {
	    std::cerr <<
		"Warning: "
		"device weighting function is already set and will be left as is."
		<< std::endl;
	}
    }

    static std::vector<size_t> get(size_t n, const std::vector<cl::CommandQueue> &queue);

    private:
	static bool is_set;
	static weight_function weight;
	static std::map<cl_device_id, double> device_weight;
};

template <bool dummy>
bool partitioning_scheme<dummy>::is_set = false;

template <bool dummy>
std::map<cl_device_id, double> partitioning_scheme<dummy>::device_weight;

template <bool dummy>
std::vector<size_t> partitioning_scheme<dummy>::get(size_t n,
	const std::vector<cl::CommandQueue> &queue)
{
    if (!is_set) {
	weight = device_vector_perf;
	is_set = true;
    }

    std::vector<size_t> part;
    part.reserve(queue.size() + 1);
    part.push_back(0);

    if (queue.size() > 1) {
	std::vector<double> cumsum;
	cumsum.reserve(queue.size() + 1);
	cumsum.push_back(0);

	for(auto q = queue.begin(); q != queue.end(); q++) {
	    cl::Context context = q->getInfo<CL_QUEUE_CONTEXT>();
	    cl::Device  device  = q->getInfo<CL_QUEUE_DEVICE>();

	    auto dw = device_weight.find(device());

	    double w = (dw == device_weight.end()) ?
		(device_weight[device()] = weight(context, device)) :
		dw->second;

	    cumsum.push_back(cumsum.back() + w);
	}

	for(uint d = 1; d < queue.size(); d++)
	    part.push_back(
		    std::min(n,
			alignup(static_cast<size_t>(n * cumsum[d] / cumsum.back()))
			)
		    );
    }

    part.push_back(n);
    return part;
}

template <bool dummy>
typename partitioning_scheme<dummy>::weight_function partitioning_scheme<dummy>::weight;

inline std::vector<size_t> partition(size_t n,
	    const std::vector<cl::CommandQueue> &queue)
{
    return partitioning_scheme<true>::get(n, queue);
}

/// Create and build a program from source string.
inline cl::Program build_sources(
	const cl::Context &context, const std::string &source
	)
{
    cl::Program program(context, cl::Program::Sources(
		1, std::make_pair(source.c_str(), source.size())
		));

    auto device = context.getInfo<CL_CONTEXT_DEVICES>();

    try {
	program.build(device);
    } catch(const cl::Error&) {
	std::cerr << source
	          << std::endl
	          << program.getBuildInfo<CL_PROGRAM_BUILD_LOG>(device[0])
	          << std::endl;
	throw;
    }

    return program;
}

/// Get maximum possible workgroup size for given kernel.
inline uint kernel_workgroup_size(
	const cl::Kernel &kernel,
	const cl::Device &device
	)
{
    size_t wgsz = 1024U;

    uint dev_wgsz = kernel.getWorkGroupInfo<CL_KERNEL_WORK_GROUP_SIZE>(device);
    while(wgsz > dev_wgsz) wgsz /= 2;

    return wgsz;
}

/// Shortcut for q.getInfo<CL_QUEUE_CONTEXT>()
inline cl::Context qctx(const cl::CommandQueue& q) {
    cl::Context ctx;
    q.getInfo(CL_QUEUE_CONTEXT, &ctx);
    return ctx;
}

/// Shortcut for q.getInfo<CL_QUEUE_DEVICE>()
inline cl::Device qdev(const cl::CommandQueue& q) {
    cl::Device dev;
    q.getInfo(CL_QUEUE_DEVICE, &dev);
    return dev;
}

} // namespace vex

/// Output description of an OpenCL error to a stream.
inline std::ostream& operator<<(std::ostream &os, const cl::Error &e) {
    os << e.what() << "(";

    switch (e.err()) {
	case 0:
	    os << "Success";
	    break;
	case -1:
	    os << "Device not found";
	    break;
	case -2:
	    os << "Device not available";
	    break;
	case -3:
	    os << "Compiler not available";
	    break;
	case -4:
	    os << "Mem object allocation failure";
	    break;
	case -5:
	    os << "Out of resources";
	    break;
	case -6:
	    os << "Out of host memory";
	    break;
	case -7:
	    os << "Profiling info not available";
	    break;
	case -8:
	    os << "Mem copy overlap";
	    break;
	case -9:
	    os << "Image format mismatch";
	    break;
	case -10:
	    os << "Image format not supported";
	    break;
	case -11:
	    os << "Build program failure";
	    break;
	case -12:
	    os << "Map failure";
	    break;
	case -13:
	    os << "Misaligned sub buffer offset";
	    break;
	case -14:
	    os << "Exec status error for events in wait list";
	    break;
	case -30:
	    os << "Invalid value";
	    break;
	case -31:
	    os << "Invalid device type";
	    break;
	case -32:
	    os << "Invalid platform";
	    break;
	case -33:
	    os << "Invalid device";
	    break;
	case -34:
	    os << "Invalid context";
	    break;
	case -35:
	    os << "Invalid queue properties";
	    break;
	case -36:
	    os << "Invalid command queue";
	    break;
	case -37:
	    os << "Invalid host ptr";
	    break;
	case -38:
	    os << "Invalid mem object";
	    break;
	case -39:
	    os << "Invalid image format descriptor";
	    break;
	case -40:
	    os << "Invalid image size";
	    break;
	case -41:
	    os << "Invalid sampler";
	    break;
	case -42:
	    os << "Invalid binary";
	    break;
	case -43:
	    os << "Invalid build options";
	    break;
	case -44:
	    os << "Invalid program";
	    break;
	case -45:
	    os << "Invalid program executable";
	    break;
	case -46:
	    os << "Invalid kernel name";
	    break;
	case -47:
	    os << "Invalid kernel definition";
	    break;
	case -48:
	    os << "Invalid kernel";
	    break;
	case -49:
	    os << "Invalid arg index";
	    break;
	case -50:
	    os << "Invalid arg value";
	    break;
	case -51:
	    os << "Invalid arg size";
	    break;
	case -52:
	    os << "Invalid kernel args";
	    break;
	case -53:
	    os << "Invalid work dimension";
	    break;
	case -54:
	    os << "Invalid work group size";
	    break;
	case -55:
	    os << "Invalid work item size";
	    break;
	case -56:
	    os << "Invalid global offset";
	    break;
	case -57:
	    os << "Invalid event wait list";
	    break;
	case -58:
	    os << "Invalid event";
	    break;
	case -59:
	    os << "Invalid operation";
	    break;
	case -60:
	    os << "Invalid gl object";
	    break;
	case -61:
	    os << "Invalid buffer size";
	    break;
	case -62:
	    os << "Invalid mip level";
	    break;
	case -63:
	    os << "Invalid global work size";
	    break;
	case -64:
	    os << "Invalid property";
	    break;
	default:
	    os << "Unknown error";
	    break;
    }

    return os << ")";
}

#ifdef WIN32
#  pragma warning(pop)
#endif
#endif
<commit_msg>doc update<commit_after>#ifndef VEXCL_UTIL_HPP
#define VEXCL_UTIL_HPP

/*
The MIT License

Copyright (c) 2012 Denis Demidov <ddemidov@ksu.ru>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

/**
 * \file   util.hpp
 * \author Denis Demidov <ddemidov@ksu.ru>
 * \brief  OpenCL general utilities.
 */

#ifdef WIN32
#  pragma warning(push)
#  pragma warning(disable : 4267 4290)
#  define NOMINMAX
#endif

#ifndef __CL_ENABLE_EXCEPTIONS
#  define __CL_ENABLE_EXCEPTIONS
#endif

#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <map>
#include <algorithm>
#include <type_traits>
#include <functional>
#include <CL/cl.hpp>
#include <climits>

typedef unsigned int  uint;
typedef unsigned char uchar;

namespace vex {

/// Convert typename to string.
template <class T> inline std::string type_name()  { return "undefined_type"; }
template <> inline std::string type_name<float>()  { return "float"; }
template <> inline std::string type_name<double>() { return "double"; }
template <> inline std::string type_name<int>()    { return "int"; }
template <> inline std::string type_name<char>()   { return "char"; }
template <> inline std::string type_name<bool>()   { return "bool"; }
template <> inline std::string type_name<uint>()   { return "unsigned int"; }
template <> inline std::string type_name<uchar>()  { return "unsigned char"; }

#if (__WORDSIZE == 64) || defined(_WIN64)
template <> inline std::string type_name<size_t>()    { return "ulong"; }
template <> inline std::string type_name<ptrdiff_t>() { return "long"; }
#endif

const std::string standard_kernel_header = std::string(
	"#if defined(cl_khr_fp64)\n"
	"#  pragma OPENCL EXTENSION cl_khr_fp64: enable\n"
	"#elif defined(cl_amd_fp64)\n"
	"#  pragma OPENCL EXTENSION cl_amd_fp64: enable\n"
	"#endif\n"
	);

/// \cond INTERNAL

/// Binary operations with their traits.
namespace binop {
    enum kind {
	Add,
	Subtract,
	Multiply,
	Divide,
	Remainder,
	Greater,
	Less,
	GreaterEqual,
	LessEqual,
	Equal,
	NotEqual,
	BitwiseAnd,
	BitwiseOr,
	BitwiseXor,
	LogicalAnd,
	LogicalOr,
	RightShift,
	LeftShift
    };

    template <kind> struct traits {};

#define BOP_TRAITS(kind, op, nm)   \
    template <> struct traits<kind> {  \
	static std::string oper() { return op; } \
	static std::string name() { return nm; } \
    };

    BOP_TRAITS(Add,          "+",  "Add_")
    BOP_TRAITS(Subtract,     "-",  "Sub_")
    BOP_TRAITS(Multiply,     "*",  "Mul_")
    BOP_TRAITS(Divide,       "/",  "Div_")
    BOP_TRAITS(Remainder,    "%",  "Mod_")
    BOP_TRAITS(Greater,      ">",  "Gtr_")
    BOP_TRAITS(Less,         "<",  "Lss_")
    BOP_TRAITS(GreaterEqual, ">=", "Geq_")
    BOP_TRAITS(LessEqual,    "<=", "Leq_")
    BOP_TRAITS(Equal,        "==", "Equ_")
    BOP_TRAITS(NotEqual,     "!=", "Neq_")
    BOP_TRAITS(BitwiseAnd,   "&",  "BAnd_")
    BOP_TRAITS(BitwiseOr,    "|",  "BOr_")
    BOP_TRAITS(BitwiseXor,   "^",  "BXor_")
    BOP_TRAITS(LogicalAnd,   "&&", "LAnd_")
    BOP_TRAITS(LogicalOr,    "||", "LOr_")
    BOP_TRAITS(RightShift,   ">>", "Rsh_")
    BOP_TRAITS(LeftShift,    "<<", "Lsh_")

#undef BOP_TRAITS
}

/// \endcond

/// Return next power of 2.
inline size_t nextpow2(size_t x) {
    --x;
    x |= x >> 1U;
    x |= x >> 2U;
    x |= x >> 4U;
    x |= x >> 8U;
    x |= x >> 16U;
    return ++x;
}

/// Align n to the next multiple of m.
inline size_t alignup(size_t n, size_t m = 16U) {
    return n % m ? n - n % m + m : n;
}

/// Weights device wrt to vector performance.
/**
 * Launches the following kernel on each device:
 * \code
 * a = b + c;
 * \endcode
 * where a, b and c are device vectors. Each device gets portion of the vector
 * proportional to the performance of this operation.
 */
inline double device_vector_perf(
	const cl::Context &context, const cl::Device &device
	);

/// Weights device wrt to spmv performance.
/**
 * Launches the following kernel on each device:
 * \code
 * y = A * x;
 * \endcode
 * where x and y are vectors, and A is matrix for 3D Poisson problem in square
 * domain. Each device gets portion of the vector proportional to the
 * performance of this operation.
 */
inline double device_spmv_perf(
	const cl::Context &context, const cl::Device &device
	);

/// Assigns equal weight to each device.
/**
 * This results in equal partitioning.
 */
inline double equal_weights(
	const cl::Context &context, const cl::Device &device
	)
{
    return 1;
}

/// Partitioning scheme for vectors and matrices.
/**
 * Should be set once before any object of vector or matrix type is declared.
 * Otherwise default parttioning function (partition_by_vector_perf) is
 * selected.
 */
template <bool dummy = true>
struct partitioning_scheme {
    typedef std::function<
	double(const cl::Context&, const cl::Device&)
	> weight_function;

    static void set(weight_function f) {
	if (!is_set) {
	    weight = f;
	    is_set = true;
	} else {
	    std::cerr <<
		"Warning: "
		"device weighting function is already set and will be left as is."
		<< std::endl;
	}
    }

    static std::vector<size_t> get(size_t n, const std::vector<cl::CommandQueue> &queue);

    private:
	static bool is_set;
	static weight_function weight;
	static std::map<cl_device_id, double> device_weight;
};

template <bool dummy>
bool partitioning_scheme<dummy>::is_set = false;

template <bool dummy>
std::map<cl_device_id, double> partitioning_scheme<dummy>::device_weight;

template <bool dummy>
std::vector<size_t> partitioning_scheme<dummy>::get(size_t n,
	const std::vector<cl::CommandQueue> &queue)
{
    if (!is_set) {
	weight = device_vector_perf;
	is_set = true;
    }

    std::vector<size_t> part;
    part.reserve(queue.size() + 1);
    part.push_back(0);

    if (queue.size() > 1) {
	std::vector<double> cumsum;
	cumsum.reserve(queue.size() + 1);
	cumsum.push_back(0);

	for(auto q = queue.begin(); q != queue.end(); q++) {
	    cl::Context context = q->getInfo<CL_QUEUE_CONTEXT>();
	    cl::Device  device  = q->getInfo<CL_QUEUE_DEVICE>();

	    auto dw = device_weight.find(device());

	    double w = (dw == device_weight.end()) ?
		(device_weight[device()] = weight(context, device)) :
		dw->second;

	    cumsum.push_back(cumsum.back() + w);
	}

	for(uint d = 1; d < queue.size(); d++)
	    part.push_back(
		    std::min(n,
			alignup(static_cast<size_t>(n * cumsum[d] / cumsum.back()))
			)
		    );
    }

    part.push_back(n);
    return part;
}

template <bool dummy>
typename partitioning_scheme<dummy>::weight_function partitioning_scheme<dummy>::weight;

inline std::vector<size_t> partition(size_t n,
	    const std::vector<cl::CommandQueue> &queue)
{
    return partitioning_scheme<true>::get(n, queue);
}

/// Create and build a program from source string.
inline cl::Program build_sources(
	const cl::Context &context, const std::string &source
	)
{
    cl::Program program(context, cl::Program::Sources(
		1, std::make_pair(source.c_str(), source.size())
		));

    auto device = context.getInfo<CL_CONTEXT_DEVICES>();

    try {
	program.build(device);
    } catch(const cl::Error&) {
	std::cerr << source
	          << std::endl
	          << program.getBuildInfo<CL_PROGRAM_BUILD_LOG>(device[0])
	          << std::endl;
	throw;
    }

    return program;
}

/// Get maximum possible workgroup size for given kernel.
inline uint kernel_workgroup_size(
	const cl::Kernel &kernel,
	const cl::Device &device
	)
{
    size_t wgsz = 1024U;

    uint dev_wgsz = kernel.getWorkGroupInfo<CL_KERNEL_WORK_GROUP_SIZE>(device);
    while(wgsz > dev_wgsz) wgsz /= 2;

    return wgsz;
}

/// Shortcut for q.getInfo<CL_QUEUE_CONTEXT>()
inline cl::Context qctx(const cl::CommandQueue& q) {
    cl::Context ctx;
    q.getInfo(CL_QUEUE_CONTEXT, &ctx);
    return ctx;
}

/// Shortcut for q.getInfo<CL_QUEUE_DEVICE>()
inline cl::Device qdev(const cl::CommandQueue& q) {
    cl::Device dev;
    q.getInfo(CL_QUEUE_DEVICE, &dev);
    return dev;
}

} // namespace vex

/// Output description of an OpenCL error to a stream.
inline std::ostream& operator<<(std::ostream &os, const cl::Error &e) {
    os << e.what() << "(";

    switch (e.err()) {
	case 0:
	    os << "Success";
	    break;
	case -1:
	    os << "Device not found";
	    break;
	case -2:
	    os << "Device not available";
	    break;
	case -3:
	    os << "Compiler not available";
	    break;
	case -4:
	    os << "Mem object allocation failure";
	    break;
	case -5:
	    os << "Out of resources";
	    break;
	case -6:
	    os << "Out of host memory";
	    break;
	case -7:
	    os << "Profiling info not available";
	    break;
	case -8:
	    os << "Mem copy overlap";
	    break;
	case -9:
	    os << "Image format mismatch";
	    break;
	case -10:
	    os << "Image format not supported";
	    break;
	case -11:
	    os << "Build program failure";
	    break;
	case -12:
	    os << "Map failure";
	    break;
	case -13:
	    os << "Misaligned sub buffer offset";
	    break;
	case -14:
	    os << "Exec status error for events in wait list";
	    break;
	case -30:
	    os << "Invalid value";
	    break;
	case -31:
	    os << "Invalid device type";
	    break;
	case -32:
	    os << "Invalid platform";
	    break;
	case -33:
	    os << "Invalid device";
	    break;
	case -34:
	    os << "Invalid context";
	    break;
	case -35:
	    os << "Invalid queue properties";
	    break;
	case -36:
	    os << "Invalid command queue";
	    break;
	case -37:
	    os << "Invalid host ptr";
	    break;
	case -38:
	    os << "Invalid mem object";
	    break;
	case -39:
	    os << "Invalid image format descriptor";
	    break;
	case -40:
	    os << "Invalid image size";
	    break;
	case -41:
	    os << "Invalid sampler";
	    break;
	case -42:
	    os << "Invalid binary";
	    break;
	case -43:
	    os << "Invalid build options";
	    break;
	case -44:
	    os << "Invalid program";
	    break;
	case -45:
	    os << "Invalid program executable";
	    break;
	case -46:
	    os << "Invalid kernel name";
	    break;
	case -47:
	    os << "Invalid kernel definition";
	    break;
	case -48:
	    os << "Invalid kernel";
	    break;
	case -49:
	    os << "Invalid arg index";
	    break;
	case -50:
	    os << "Invalid arg value";
	    break;
	case -51:
	    os << "Invalid arg size";
	    break;
	case -52:
	    os << "Invalid kernel args";
	    break;
	case -53:
	    os << "Invalid work dimension";
	    break;
	case -54:
	    os << "Invalid work group size";
	    break;
	case -55:
	    os << "Invalid work item size";
	    break;
	case -56:
	    os << "Invalid global offset";
	    break;
	case -57:
	    os << "Invalid event wait list";
	    break;
	case -58:
	    os << "Invalid event";
	    break;
	case -59:
	    os << "Invalid operation";
	    break;
	case -60:
	    os << "Invalid gl object";
	    break;
	case -61:
	    os << "Invalid buffer size";
	    break;
	case -62:
	    os << "Invalid mip level";
	    break;
	case -63:
	    os << "Invalid global work size";
	    break;
	case -64:
	    os << "Invalid property";
	    break;
	default:
	    os << "Unknown error";
	    break;
    }

    return os << ")";
}

#ifdef WIN32
#  pragma warning(pop)
#endif
#endif
<|endoftext|>


<commit_before>//
//  The MIT License (MIT)
//
//  Copyright (c) 2016 Tag Games Limited
//
//  Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//  THE SOFTWARE.
//

#include <CSBackend/Rendering/OpenGL/Material/GLMaterial.h>

#include <CSBackend/Rendering/OpenGL/Camera/GLCamera.h>
#include <CSBackend/Rendering/OpenGL/Shader/GLShader.h>

#include <ChilliSource/Core/Base/Colour.h>
#include <ChilliSource/Rendering/Base/BlendMode.h>
#include <ChilliSource/Rendering/Base/StencilOp.h>
#include <ChilliSource/Rendering/Base/TestFunc.h>
#include <ChilliSource/Rendering/Material/RenderMaterial.h>

namespace CSBackend
{
    namespace OpenGL
    {
        namespace
        {
            const std::string k_uniformEmissive = "u_emissive";
            const std::string k_uniformAmbient = "u_ambient";
            const std::string k_uniformDiffuse = "u_diffuse";
            const std::string k_uniformSpecular = "u_specular";
            const std::string k_uniformTexturePrefix = "u_texture";
            const std::string k_uniformCubemapPrefix = "u_cubemap";

            /// Converts from a ChilliSource blend mode to an OpenGL blend mode.
            ///
            /// @param blendMode
            ///     The ChilliSource blend mode.
            ///
            /// @return The OpenGL blend mode.
            ///
            GLenum ToGLBlendMode(ChilliSource::BlendMode blendMode)
            {
                switch(blendMode)
                {
                    case ChilliSource::BlendMode::k_zero:
                        return GL_ZERO;
                    case ChilliSource::BlendMode::k_one:
                        return GL_ONE;
                    case ChilliSource::BlendMode::k_sourceCol:
                        return GL_SRC_COLOR;
                    case ChilliSource::BlendMode::k_oneMinusSourceCol:
                        return GL_ONE_MINUS_SRC_COLOR;
                    case ChilliSource::BlendMode::k_sourceAlpha:
                        return GL_SRC_ALPHA;
                    case ChilliSource::BlendMode::k_oneMinusSourceAlpha:
                        return GL_ONE_MINUS_SRC_ALPHA;
                    case ChilliSource::BlendMode::k_destCol:
                        return GL_DST_COLOR;
                    case ChilliSource::BlendMode::k_oneMinusDestCol:
                        return GL_ONE_MINUS_DST_COLOR;
                    case ChilliSource::BlendMode::k_destAlpha:
                        return GL_DST_ALPHA;
                    case ChilliSource::BlendMode::k_oneMinusDestAlpha:
                        return GL_ONE_MINUS_DST_ALPHA;
                    default:
                        CS_LOG_FATAL("Invalid blend mode.");
                        return GL_ZERO;
                };
            }

            /// Converts from a ChilliSource stencil op to an OpenGL one
            ///
            /// @param stencilOp
            ///     The ChilliSource stencil op
            ///
            /// @return The OpenGL stencil op.
            ///
            GLenum ToGLStencilOp(ChilliSource::StencilOp stencilOp)
            {
                switch(stencilOp)
                {
                    case ChilliSource::StencilOp::k_keep:
                        return GL_KEEP;
                    case ChilliSource::StencilOp::k_zero:
                        return GL_ZERO;
                    case ChilliSource::StencilOp::k_replace:
                        return GL_REPLACE;
                    case ChilliSource::StencilOp::k_increment:
                        return GL_INCR;
                    case ChilliSource::StencilOp::k_incrementWrap:
                        return GL_INCR_WRAP;
                    case ChilliSource::StencilOp::k_decrement:
                        return GL_DECR;
                    case ChilliSource::StencilOp::k_decrementWrap:
                        return GL_DECR_WRAP;
                    case ChilliSource::StencilOp::k_invert:
                        return GL_INVERT;
                    default:
                        CS_LOG_FATAL("Invalid stencil op.");
                        return GL_KEEP;
                };
            }

            /// Converts from a ChilliSource test func to an OpenGL one
            ///
            /// @param testFunc
            ///     The ChilliSource test func
            ///
            /// @return The OpenGL test func.
            ///
            GLenum ToGLTestFunc(ChilliSource::TestFunc testFunc)
            {
                switch(testFunc)
                {
                    case ChilliSource::TestFunc::k_never:
                        return GL_NEVER;
                    case ChilliSource::TestFunc::k_less:
                        return GL_LESS;
                    case ChilliSource::TestFunc::k_lessEqual:
                        return GL_LEQUAL;
                    case ChilliSource::TestFunc::k_greater:
                        return GL_GREATER;
                    case ChilliSource::TestFunc::k_greaterEqual:
                        return GL_GEQUAL;
                    case ChilliSource::TestFunc::k_equal:
                        return GL_EQUAL;
                    case ChilliSource::TestFunc::k_notEqual:
                        return GL_NOTEQUAL;
                    case ChilliSource::TestFunc::k_always:
                        return GL_ALWAYS;
                    default:
                        CS_LOG_FATAL("Invalid test func.");
                        return GL_ALWAYS;
                };
            }

            /// Applies the given batch of custom shader variables to the given shader. If
            /// any of the variables do not exist in the shader, this will assert.
            ///
            /// @param renderShaderVariables
            ///     The shader variables to apply.
            /// @param glShader
            ///     The shader to apply the variables to.
            ///
            void ApplyCustomShaderVariables(const ChilliSource::RenderShaderVariables* renderShaderVariables, GLShader* glShader) noexcept
            {
                CS_ASSERT(renderShaderVariables, "Cannot apply null shader variables.");
                CS_ASSERT(glShader, "Cannot apply shader variables to null shader.");

                for (const auto& pair : renderShaderVariables->GetFloatVariables())
                {
                    glShader->SetUniform(pair.first, pair.second);
                }

                for (const auto& pair : renderShaderVariables->GetVector2Variables())
                {
                    glShader->SetUniform(pair.first, pair.second);
                }

                for (const auto& pair : renderShaderVariables->GetVector3Variables())
                {
                    glShader->SetUniform(pair.first, pair.second);
                }

                for (const auto& pair : renderShaderVariables->GetVector4Variables())
                {
                    glShader->SetUniform(pair.first, pair.second);
                }

                for (const auto& pair : renderShaderVariables->GetMatrix4Variables())
                {
                    glShader->SetUniform(pair.first, pair.second);
                }

                for (const auto& pair : renderShaderVariables->GetColourVariables())
                {
                    glShader->SetUniform(pair.first, pair.second);
                }
            }
        }

        //------------------------------------------------------------------------------
        void GLMaterial::Apply(const ChilliSource::RenderMaterial* renderMaterial, GLShader* glShader) noexcept
        {
            renderMaterial->IsDepthWriteEnabled() ? glDepthMask(GL_TRUE) : glDepthMask(GL_FALSE);
            renderMaterial->IsColourWriteEnabled() ? glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE) : glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);

            if(renderMaterial->IsDepthTestEnabled())
            {
                glEnable(GL_DEPTH_TEST);
                glDepthFunc(ToGLTestFunc(renderMaterial->GetDepthTestFunc()));
            }
            else
            {
                glDisable(GL_DEPTH_TEST);
            }

            if (renderMaterial->IsFaceCullingEnabled())
            {
                glEnable(GL_CULL_FACE);
                glCullFace(GL_BACK);
            }
            else
            {
                glDisable(GL_CULL_FACE);
            }

            if (renderMaterial->IsTransparencyEnabled())
            {
                glEnable(GL_BLEND);
                glBlendFunc(ToGLBlendMode(renderMaterial->GetSourceBlendMode()), ToGLBlendMode(renderMaterial->GetDestinationBlendMode()));
            }
            else
            {
                glDisable(GL_BLEND);
            }

            if(renderMaterial->IsStencilTestEnabled())
            {
                glEnable(GL_STENCIL_TEST);
                glStencilOp(ToGLStencilOp(renderMaterial->GetStencilFailOp()), ToGLStencilOp(renderMaterial->GetStencilDepthFailOp()), ToGLStencilOp(renderMaterial->GetStencilPassOp()));
                glStencilFunc(ToGLTestFunc(renderMaterial->GetStencilTestFunc()), (GLint)renderMaterial->GetStencilTestFuncRef(), (GLuint)renderMaterial->GetStencilTestFuncMask());
            }
            else
            {
                glDisable(GL_STENCIL_TEST);
            }

            s32 samplerNumber = 0;

            for (auto i = 0; i < renderMaterial->GetRenderTextures2D().size(); ++i, ++samplerNumber)
            {
                glShader->SetUniform(k_uniformTexturePrefix + ChilliSource::ToString(i), samplerNumber);
            }

            for (auto i = 0; i < renderMaterial->GetRenderTexturesCubemap().size(); ++i, ++samplerNumber)
            {
                glShader->SetUniform(k_uniformCubemapPrefix + ChilliSource::ToString(i), samplerNumber);
            }

            glShader->SetUniform(k_uniformEmissive, renderMaterial->GetEmissiveColour(), GLShader::FailurePolicy::k_silent);
            glShader->SetUniform(k_uniformAmbient, renderMaterial->GetAmbientColour(), GLShader::FailurePolicy::k_silent);
            glShader->SetUniform(k_uniformDiffuse, renderMaterial->GetDiffuseColour(), GLShader::FailurePolicy::k_silent);
            glShader->SetUniform(k_uniformSpecular, renderMaterial->GetSpecularColour(), GLShader::FailurePolicy::k_silent);

            if (renderMaterial->GetRenderShaderVariables())
            {
                ApplyCustomShaderVariables(renderMaterial->GetRenderShaderVariables(), glShader);
            }
        }
    }
}
<commit_msg>Fixing issue where cull face was always set to back and ignoring the material<commit_after>//
//  The MIT License (MIT)
//
//  Copyright (c) 2016 Tag Games Limited
//
//  Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//  THE SOFTWARE.
//

#include <CSBackend/Rendering/OpenGL/Material/GLMaterial.h>

#include <CSBackend/Rendering/OpenGL/Camera/GLCamera.h>
#include <CSBackend/Rendering/OpenGL/Shader/GLShader.h>

#include <ChilliSource/Core/Base/Colour.h>
#include <ChilliSource/Rendering/Base/BlendMode.h>
#include <ChilliSource/Rendering/Base/CullFace.h>
#include <ChilliSource/Rendering/Base/StencilOp.h>
#include <ChilliSource/Rendering/Base/TestFunc.h>
#include <ChilliSource/Rendering/Material/RenderMaterial.h>

namespace CSBackend
{
    namespace OpenGL
    {
        namespace
        {
            const std::string k_uniformEmissive = "u_emissive";
            const std::string k_uniformAmbient = "u_ambient";
            const std::string k_uniformDiffuse = "u_diffuse";
            const std::string k_uniformSpecular = "u_specular";
            const std::string k_uniformTexturePrefix = "u_texture";
            const std::string k_uniformCubemapPrefix = "u_cubemap";

            /// Converts from a ChilliSource blend mode to an OpenGL blend mode.
            ///
            /// @param blendMode
            ///     The ChilliSource blend mode.
            ///
            /// @return The OpenGL blend mode.
            ///
            GLenum ToGLBlendMode(ChilliSource::BlendMode blendMode)
            {
                switch(blendMode)
                {
                    case ChilliSource::BlendMode::k_zero:
                        return GL_ZERO;
                    case ChilliSource::BlendMode::k_one:
                        return GL_ONE;
                    case ChilliSource::BlendMode::k_sourceCol:
                        return GL_SRC_COLOR;
                    case ChilliSource::BlendMode::k_oneMinusSourceCol:
                        return GL_ONE_MINUS_SRC_COLOR;
                    case ChilliSource::BlendMode::k_sourceAlpha:
                        return GL_SRC_ALPHA;
                    case ChilliSource::BlendMode::k_oneMinusSourceAlpha:
                        return GL_ONE_MINUS_SRC_ALPHA;
                    case ChilliSource::BlendMode::k_destCol:
                        return GL_DST_COLOR;
                    case ChilliSource::BlendMode::k_oneMinusDestCol:
                        return GL_ONE_MINUS_DST_COLOR;
                    case ChilliSource::BlendMode::k_destAlpha:
                        return GL_DST_ALPHA;
                    case ChilliSource::BlendMode::k_oneMinusDestAlpha:
                        return GL_ONE_MINUS_DST_ALPHA;
                    default:
                        CS_LOG_FATAL("Invalid blend mode.");
                        return GL_ZERO;
                };
            }

            /// Converts from a ChilliSource stencil op to an OpenGL one
            ///
            /// @param stencilOp
            ///     The ChilliSource stencil op
            ///
            /// @return The OpenGL stencil op.
            ///
            GLenum ToGLStencilOp(ChilliSource::StencilOp stencilOp)
            {
                switch(stencilOp)
                {
                    case ChilliSource::StencilOp::k_keep:
                        return GL_KEEP;
                    case ChilliSource::StencilOp::k_zero:
                        return GL_ZERO;
                    case ChilliSource::StencilOp::k_replace:
                        return GL_REPLACE;
                    case ChilliSource::StencilOp::k_increment:
                        return GL_INCR;
                    case ChilliSource::StencilOp::k_incrementWrap:
                        return GL_INCR_WRAP;
                    case ChilliSource::StencilOp::k_decrement:
                        return GL_DECR;
                    case ChilliSource::StencilOp::k_decrementWrap:
                        return GL_DECR_WRAP;
                    case ChilliSource::StencilOp::k_invert:
                        return GL_INVERT;
                    default:
                        CS_LOG_FATAL("Invalid stencil op.");
                        return GL_KEEP;
                };
            }

            /// Converts from a ChilliSource test func to an OpenGL one
            ///
            /// @param testFunc
            ///     The ChilliSource test func
            ///
            /// @return The OpenGL test func.
            ///
            GLenum ToGLTestFunc(ChilliSource::TestFunc testFunc)
            {
                switch(testFunc)
                {
                    case ChilliSource::TestFunc::k_never:
                        return GL_NEVER;
                    case ChilliSource::TestFunc::k_less:
                        return GL_LESS;
                    case ChilliSource::TestFunc::k_lessEqual:
                        return GL_LEQUAL;
                    case ChilliSource::TestFunc::k_greater:
                        return GL_GREATER;
                    case ChilliSource::TestFunc::k_greaterEqual:
                        return GL_GEQUAL;
                    case ChilliSource::TestFunc::k_equal:
                        return GL_EQUAL;
                    case ChilliSource::TestFunc::k_notEqual:
                        return GL_NOTEQUAL;
                    case ChilliSource::TestFunc::k_always:
                        return GL_ALWAYS;
                    default:
                        CS_LOG_FATAL("Invalid test func.");
                        return GL_ALWAYS;
                };
            }

            /// Converts from a ChilliSource cull face to an OpenGL one
            ///
            /// @param cullFace
            ///     The ChilliSource cull face
            ///
            /// @return The OpenGL cull face.
            ///
            GLenum ToGLCullFace(ChilliSource::CullFace cullFace)
            {
                switch(cullFace)
                {
                    case ChilliSource::CullFace::k_front:
                        return GL_FRONT;
                    case ChilliSource::CullFace::k_back:
                        return GL_BACK;
                }
            }

            /// Applies the given batch of custom shader variables to the given shader. If
            /// any of the variables do not exist in the shader, this will assert.
            ///
            /// @param renderShaderVariables
            ///     The shader variables to apply.
            /// @param glShader
            ///     The shader to apply the variables to.
            ///
            void ApplyCustomShaderVariables(const ChilliSource::RenderShaderVariables* renderShaderVariables, GLShader* glShader) noexcept
            {
                CS_ASSERT(renderShaderVariables, "Cannot apply null shader variables.");
                CS_ASSERT(glShader, "Cannot apply shader variables to null shader.");

                for (const auto& pair : renderShaderVariables->GetFloatVariables())
                {
                    glShader->SetUniform(pair.first, pair.second);
                }

                for (const auto& pair : renderShaderVariables->GetVector2Variables())
                {
                    glShader->SetUniform(pair.first, pair.second);
                }

                for (const auto& pair : renderShaderVariables->GetVector3Variables())
                {
                    glShader->SetUniform(pair.first, pair.second);
                }

                for (const auto& pair : renderShaderVariables->GetVector4Variables())
                {
                    glShader->SetUniform(pair.first, pair.second);
                }

                for (const auto& pair : renderShaderVariables->GetMatrix4Variables())
                {
                    glShader->SetUniform(pair.first, pair.second);
                }

                for (const auto& pair : renderShaderVariables->GetColourVariables())
                {
                    glShader->SetUniform(pair.first, pair.second);
                }
            }
        }

        //------------------------------------------------------------------------------
        void GLMaterial::Apply(const ChilliSource::RenderMaterial* renderMaterial, GLShader* glShader) noexcept
        {
            renderMaterial->IsDepthWriteEnabled() ? glDepthMask(GL_TRUE) : glDepthMask(GL_FALSE);
            renderMaterial->IsColourWriteEnabled() ? glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE) : glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);

            if(renderMaterial->IsDepthTestEnabled())
            {
                glEnable(GL_DEPTH_TEST);
                glDepthFunc(ToGLTestFunc(renderMaterial->GetDepthTestFunc()));
            }
            else
            {
                glDisable(GL_DEPTH_TEST);
            }

            if (renderMaterial->IsFaceCullingEnabled())
            {
                glEnable(GL_CULL_FACE);
                glCullFace(ToGLCullFace(renderMaterial->GetCullFace()));
            }
            else
            {
                glDisable(GL_CULL_FACE);
            }

            if (renderMaterial->IsTransparencyEnabled())
            {
                glEnable(GL_BLEND);
                glBlendFunc(ToGLBlendMode(renderMaterial->GetSourceBlendMode()), ToGLBlendMode(renderMaterial->GetDestinationBlendMode()));
            }
            else
            {
                glDisable(GL_BLEND);
            }

            if(renderMaterial->IsStencilTestEnabled())
            {
                glEnable(GL_STENCIL_TEST);
                glStencilOp(ToGLStencilOp(renderMaterial->GetStencilFailOp()), ToGLStencilOp(renderMaterial->GetStencilDepthFailOp()), ToGLStencilOp(renderMaterial->GetStencilPassOp()));
                glStencilFunc(ToGLTestFunc(renderMaterial->GetStencilTestFunc()), (GLint)renderMaterial->GetStencilTestFuncRef(), (GLuint)renderMaterial->GetStencilTestFuncMask());
            }
            else
            {
                glDisable(GL_STENCIL_TEST);
            }

            s32 samplerNumber = 0;

            for (auto i = 0; i < renderMaterial->GetRenderTextures2D().size(); ++i, ++samplerNumber)
            {
                glShader->SetUniform(k_uniformTexturePrefix + ChilliSource::ToString(i), samplerNumber);
            }

            for (auto i = 0; i < renderMaterial->GetRenderTexturesCubemap().size(); ++i, ++samplerNumber)
            {
                glShader->SetUniform(k_uniformCubemapPrefix + ChilliSource::ToString(i), samplerNumber);
            }

            glShader->SetUniform(k_uniformEmissive, renderMaterial->GetEmissiveColour(), GLShader::FailurePolicy::k_silent);
            glShader->SetUniform(k_uniformAmbient, renderMaterial->GetAmbientColour(), GLShader::FailurePolicy::k_silent);
            glShader->SetUniform(k_uniformDiffuse, renderMaterial->GetDiffuseColour(), GLShader::FailurePolicy::k_silent);
            glShader->SetUniform(k_uniformSpecular, renderMaterial->GetSpecularColour(), GLShader::FailurePolicy::k_silent);

            if (renderMaterial->GetRenderShaderVariables())
            {
                ApplyCustomShaderVariables(renderMaterial->GetRenderShaderVariables(), glShader);
            }
        }
    }
}
<|endoftext|>


<commit_before>#include "vm.hpp"
#include "console.hpp"

#include "object_utils.hpp"

#include "builtin/array.hpp"
#include "builtin/class.hpp"
#include "builtin/fsevent.hpp"
#include "builtin/string.hpp"
#include "builtin/thread.hpp"

#include "dtrace/dtrace.h"

#include "util/file.hpp"
#include "util/logger.hpp"

#include <fcntl.h>
#include <sys/file.h>

#include <stdio.h>

// read
#include <unistd.h>
#include <sys/uio.h>
#include <sys/types.h>

namespace rubinius {
  using namespace utilities;

  namespace console {
    Object* console_request_trampoline(STATE) {
      state->shared().console()->process_requests(state);
      GCTokenImpl gct;
      state->gc_dependent(gct, 0);
      return cNil;
    }

    Object* console_response_trampoline(STATE) {
      state->shared().console()->process_responses(state);
      GCTokenImpl gct;
      state->gc_dependent(gct, 0);
      return cNil;
    }

    Console::Console(STATE)
      : AuxiliaryThread()
      , shared_(state->shared())
      , request_vm_(NULL)
      , response_vm_(NULL)
      , request_(state)
      , response_(state)
      , console_(state)
      , fsevent_(state)
      , request_fd_(-1)
      , response_fd_(-1)
      , request_exit_(false)
      , response_exit_(false)
      , request_list_(0)
    {
      shared_.auxiliary_threads()->register_thread(this);
    }

    Console::~Console() {
      shared_.auxiliary_threads()->unregister_thread(this);
    }

    void Console::wakeup() {
      request_exit_ = true;
      if(write(request_fd_, "x", 1) < 0) {
        logger::error("%s: console: unable to wake request thread", strerror(errno));
      }
    }

    void Console::cleanup(bool remove_files) {
      if(request_fd_ > 0) {
        close(request_fd_);
        if(remove_files) unlink(request_path_.c_str());
        request_fd_ = -1;
      }

      if(response_fd_ > 0) {
        close(response_fd_);
        if(remove_files) unlink(response_path_.c_str());
        response_fd_ = -1;
      }

      if(request_list_) {
        for(RequestList::const_iterator i = request_list_->begin();
            i != request_list_->end();
            ++i)
        {
          delete[] *i;
        }

        delete request_list_;
        request_list_ = NULL;
      }
    }

    void Console::initialize(STATE) {
      request_exit_ = false;
      response_exit_ = false;

      list_lock_.init();
      response_lock_.init();
      response_cond_.init();

      std::string path(shared_.fsapi_path + "/console");

      request_path_ = path + "-request";
      response_path_ = path + "-response";

      request_list_ = new RequestList;

      Module* mod = as<Module>(G(rubinius)->get_const(state, "Console"));
      Class* cls = as<Class>(mod->get_const(state, "Server"));
      console_.set(cls->send(state, 0, state->symbol("new")));

      cls->set_const(state, state->symbol("RequestPath"),
          String::create(state, request_path_.c_str()));
      cls->set_const(state, state->symbol("ResponsePath"),
          String::create(state, response_path_.c_str()));
    }

    static int open_file(std::string path) {
      int fd = ::open(path.c_str(), O_CREAT | O_TRUNC | O_RDWR, 0666);

      if(fd < 0) {
        logger::error("%s: console: unable to open: %s", strerror(errno), path.c_str());
      }

      return fd;
    }

    void Console::setup_files(STATE) {
      request_fd_ = open_file(request_path_);
      response_fd_ = open_file(response_path_);

      FSEvent* fsevent = FSEvent::create(state);
      fsevent->watch_file(state, request_fd_, request_path_.c_str());
      fsevent_.set(fsevent);
    }

    void Console::start(STATE) {
      initialize(state);
      setup_files(state);
      start_threads(state);
    }

    void Console::run(STATE) {
      if(request_.get()->fork_attached(state)) {
        rubinius::bug("Unable to start console request thread");
      }

      if(response_.get()->fork_attached(state)) {
        rubinius::bug("Unable to start console response thread");
      }
    }

    void Console::start_threads(STATE) {
      SYNC(state);

      // Don't start threads if we couldn't open communication channels.
      if(request_fd_ < 0 || response_fd_ < 0) return;

      if(!request_vm_) {
        request_vm_ = state->shared().new_vm();
        request_vm_->metrics()->init(metrics::eConsoleMetrics);
        request_exit_ = false;
        request_.set(Thread::create(state, request_vm_, G(thread),
                     console_request_trampoline, true));
      }

      if(!response_vm_) {
        response_vm_ = state->shared().new_vm();
        response_vm_->metrics()->init(metrics::eConsoleMetrics);
        response_exit_ = false;
        response_.set(Thread::create(state, response_vm_, G(thread),
                     console_response_trampoline, true));
      }

      if(request_vm_ && response_vm_) {
        run(state);
      }
    }

    void Console::stop_threads(STATE) {
      SYNC(state);

      if(request_vm_) {
        file::LockGuard guard(request_fd_, LOCK_EX);

        if(guard.status() == file::eLockSucceeded) {
          logger::error("%s: console: unable to lock request file", strerror(errno));
          return;
        }

        wakeup();

        pthread_t os = request_vm_->os_thread();
        request_exit_ = true;

        void* return_value;
        pthread_join(os, &return_value);

        request_vm_ = NULL;
      }

      if(response_vm_) {
        file::LockGuard guard(response_fd_, LOCK_EX);

        if(guard.status() != file::eLockSucceeded) {
          logger::error("%s: unable to lock response file", strerror(errno));
          return;
        }

        pthread_t os = response_vm_->os_thread();
        response_exit_ = true;

        response_cond_.signal();

        void* return_value;
        pthread_join(os, &return_value);

        response_vm_ = NULL;
      }
    }

    void Console::shutdown(STATE) {
      stop_threads(state);
      cleanup(true);
    }

    void Console::before_exec(STATE) {
      stop_threads(state);
      cleanup(true);
    }

    void Console::after_exec(STATE) {
      setup_files(state);
      start_threads(state);
    }

    void Console::before_fork(STATE) {
      stop_threads(state);
    }

    void Console::after_fork_parent(STATE) {
      start_threads(state);
    }

    void Console::after_fork_child(STATE) {
      if(request_vm_) {
        VM::discard(state, request_vm_);
        request_vm_ = NULL;
      }

      if(response_vm_) {
        VM::discard(state, response_vm_);
        response_vm_ = NULL;
      }

      cleanup(false);
      start(state);
    }

    char* Console::read_request(STATE) {
      file::LockGuard guard(request_fd_, LOCK_EX);

      if(guard.status() != file::eLockSucceeded) {
        logger::error("%s: console: unable to lock request file", strerror(errno));
        return NULL;
      }

      char* buf[1024];

      lseek(request_fd_, 0, SEEK_SET);
      ssize_t bytes = ::read(request_fd_, buf, 1024);

      char* req = NULL;
      if(bytes > 0) {
        req = new char[bytes+1];
        memcpy(req, buf, bytes);
        req[bytes] = 0;
        request_vm_->metrics()->m.console_metrics.requests_received++;
      } else if(bytes < 0) {
        logger::error("%s: console: unable to read request", strerror(errno));
      }

      if(lseek(request_fd_, 0, SEEK_SET) < 0) {
        logger::error("%s: console: unable to rewind request file", strerror(errno));
      }
      if(ftruncate(request_fd_, 0) < 0) {
        logger::error("%s: console: unable to truncate request file", strerror(errno));
      }

      return req;
    }

    void Console::process_requests(STATE) {
      GCTokenImpl gct;
      RBX_DTRACE_CONST char* thread_name =
        const_cast<RBX_DTRACE_CONST char*>("rbx.console.request");
      request_vm_->set_name(thread_name);

      RUBINIUS_THREAD_START(const_cast<RBX_DTRACE_CONST char*>(thread_name),
                            state->vm()->thread_id(), 1);

      state->vm()->thread->hard_unlock(state, gct, 0);
      state->gc_independent(gct, 0);

      while(!request_exit_) {
        Object* status = fsevent_.get()->wait_for_event(state);

        if(request_exit_) break;
        if(status->nil_p()) continue;

        char* request = read_request(state);

        if(request) {
          utilities::thread::Mutex::LockGuard lg(list_lock_);

          request_list_->push_back(request);
          response_cond_.signal();
        }
      }

      RUBINIUS_THREAD_STOP(const_cast<RBX_DTRACE_CONST char*>(thread_name),
                           state->vm()->thread_id(), 1);
    }

    void Console::write_response(STATE, const char* response, native_int size) {
      file::LockGuard guard(response_fd_, LOCK_EX);

      if(guard.status() != file::eLockSucceeded) {
        logger::error("%s: unable to lock response file", strerror(errno));
        return;
      }

      if(lseek(response_fd_, 0, SEEK_SET) < 0) {
        logger::error("%s: console: unable to rewind response file", strerror(errno));
        return;
      }
      if(ftruncate(response_fd_, 0) < 0) {
        logger::error("%s: console: unable to truncate response file", strerror(errno));
        return;
      }

      if(::write(response_fd_, response, size) < 0) {
        logger::error("%s: console: unable to write response", strerror(errno));
      }

      response_vm_->metrics()->m.console_metrics.responses_sent++;
    }

    void Console::process_responses(STATE) {
      GCTokenImpl gct;
      RBX_DTRACE_CONST char* thread_name =
        const_cast<RBX_DTRACE_CONST char*>("rbx.console.response");
      response_vm_->set_name(thread_name);

      RUBINIUS_THREAD_START(const_cast<RBX_DTRACE_CONST char*>(thread_name),
                            state->vm()->thread_id(), 1);

      state->vm()->thread->hard_unlock(state, gct, 0);
      state->gc_dependent(gct, 0);

      char* request = NULL;

      while(!response_exit_) {
        {
          utilities::thread::Mutex::LockGuard lg(list_lock_);
          GCIndependent guard(state, 0);

          if(request_list_->size() > 0) {
            request = request_list_->back();
            request_list_->pop_back();
          }
        }

        if(response_exit_) break;

        if(request) {
          Array* args = Array::create(state, 1);
          args->aset(state, 0, String::create(state, request));
          Object* result = console_.get()->send(state, 0, state->symbol("evaluate"),
              args, cNil);

          if(String* response = try_as<String>(result)) {
            GCIndependent guard(state, 0);

            write_response(state,
                reinterpret_cast<const char*>(response->byte_address()),
                response->byte_size());
          }

          request = NULL;
        } else {
          utilities::thread::Mutex::LockGuard lg(response_lock_);
          GCIndependent guard(state, 0);

          response_cond_.wait(response_lock_);
        }
      }

      RUBINIUS_THREAD_STOP(const_cast<RBX_DTRACE_CONST char*>(thread_name),
                           state->vm()->thread_id(), 1);
    }
  }
}
<commit_msg>Revert "Lock when calling Console::stop_threads()"<commit_after>#include "vm.hpp"
#include "console.hpp"

#include "object_utils.hpp"

#include "builtin/array.hpp"
#include "builtin/class.hpp"
#include "builtin/fsevent.hpp"
#include "builtin/string.hpp"
#include "builtin/thread.hpp"

#include "dtrace/dtrace.h"

#include "util/file.hpp"
#include "util/logger.hpp"

#include <fcntl.h>
#include <sys/file.h>

#include <stdio.h>

// read
#include <unistd.h>
#include <sys/uio.h>
#include <sys/types.h>

namespace rubinius {
  using namespace utilities;

  namespace console {
    Object* console_request_trampoline(STATE) {
      state->shared().console()->process_requests(state);
      GCTokenImpl gct;
      state->gc_dependent(gct, 0);
      return cNil;
    }

    Object* console_response_trampoline(STATE) {
      state->shared().console()->process_responses(state);
      GCTokenImpl gct;
      state->gc_dependent(gct, 0);
      return cNil;
    }

    Console::Console(STATE)
      : AuxiliaryThread()
      , shared_(state->shared())
      , request_vm_(NULL)
      , response_vm_(NULL)
      , request_(state)
      , response_(state)
      , console_(state)
      , fsevent_(state)
      , request_fd_(-1)
      , response_fd_(-1)
      , request_exit_(false)
      , response_exit_(false)
      , request_list_(0)
    {
      shared_.auxiliary_threads()->register_thread(this);
    }

    Console::~Console() {
      shared_.auxiliary_threads()->unregister_thread(this);
    }

    void Console::wakeup() {
      request_exit_ = true;
      if(write(request_fd_, "x", 1) < 0) {
        logger::error("%s: console: unable to wake request thread", strerror(errno));
      }
    }

    void Console::cleanup(bool remove_files) {
      if(request_fd_ > 0) {
        close(request_fd_);
        if(remove_files) unlink(request_path_.c_str());
        request_fd_ = -1;
      }

      if(response_fd_ > 0) {
        close(response_fd_);
        if(remove_files) unlink(response_path_.c_str());
        response_fd_ = -1;
      }

      if(request_list_) {
        for(RequestList::const_iterator i = request_list_->begin();
            i != request_list_->end();
            ++i)
        {
          delete[] *i;
        }

        delete request_list_;
        request_list_ = NULL;
      }
    }

    void Console::initialize(STATE) {
      request_exit_ = false;
      response_exit_ = false;

      list_lock_.init();
      response_lock_.init();
      response_cond_.init();

      std::string path(shared_.fsapi_path + "/console");

      request_path_ = path + "-request";
      response_path_ = path + "-response";

      request_list_ = new RequestList;

      Module* mod = as<Module>(G(rubinius)->get_const(state, "Console"));
      Class* cls = as<Class>(mod->get_const(state, "Server"));
      console_.set(cls->send(state, 0, state->symbol("new")));

      cls->set_const(state, state->symbol("RequestPath"),
          String::create(state, request_path_.c_str()));
      cls->set_const(state, state->symbol("ResponsePath"),
          String::create(state, response_path_.c_str()));
    }

    static int open_file(std::string path) {
      int fd = ::open(path.c_str(), O_CREAT | O_TRUNC | O_RDWR, 0666);

      if(fd < 0) {
        logger::error("%s: console: unable to open: %s", strerror(errno), path.c_str());
      }

      return fd;
    }

    void Console::setup_files(STATE) {
      request_fd_ = open_file(request_path_);
      response_fd_ = open_file(response_path_);

      FSEvent* fsevent = FSEvent::create(state);
      fsevent->watch_file(state, request_fd_, request_path_.c_str());
      fsevent_.set(fsevent);
    }

    void Console::start(STATE) {
      initialize(state);
      setup_files(state);
      start_threads(state);
    }

    void Console::run(STATE) {
      if(request_.get()->fork_attached(state)) {
        rubinius::bug("Unable to start console request thread");
      }

      if(response_.get()->fork_attached(state)) {
        rubinius::bug("Unable to start console response thread");
      }
    }

    void Console::start_threads(STATE) {
      SYNC(state);

      // Don't start threads if we couldn't open communication channels.
      if(request_fd_ < 0 || response_fd_ < 0) return;

      if(!request_vm_) {
        request_vm_ = state->shared().new_vm();
        request_vm_->metrics()->init(metrics::eConsoleMetrics);
        request_exit_ = false;
        request_.set(Thread::create(state, request_vm_, G(thread),
                     console_request_trampoline, true));
      }

      if(!response_vm_) {
        response_vm_ = state->shared().new_vm();
        response_vm_->metrics()->init(metrics::eConsoleMetrics);
        response_exit_ = false;
        response_.set(Thread::create(state, response_vm_, G(thread),
                     console_response_trampoline, true));
      }

      if(request_vm_ && response_vm_) {
        run(state);
      }
    }

    void Console::stop_threads(STATE) {
      SYNC(state);

      if(request_vm_) {
        wakeup();

        pthread_t os = request_vm_->os_thread();
        request_exit_ = true;

        void* return_value;
        pthread_join(os, &return_value);

        request_vm_ = NULL;
      }

      if(response_vm_) {
        pthread_t os = response_vm_->os_thread();
        response_exit_ = true;

        response_cond_.signal();

        void* return_value;
        pthread_join(os, &return_value);

        response_vm_ = NULL;
      }
    }

    void Console::shutdown(STATE) {
      stop_threads(state);
      cleanup(true);
    }

    void Console::before_exec(STATE) {
      stop_threads(state);
      cleanup(true);
    }

    void Console::after_exec(STATE) {
      setup_files(state);
      start_threads(state);
    }

    void Console::before_fork(STATE) {
      stop_threads(state);
    }

    void Console::after_fork_parent(STATE) {
      start_threads(state);
    }

    void Console::after_fork_child(STATE) {
      if(request_vm_) {
        VM::discard(state, request_vm_);
        request_vm_ = NULL;
      }

      if(response_vm_) {
        VM::discard(state, response_vm_);
        response_vm_ = NULL;
      }

      cleanup(false);
      start(state);
    }

    char* Console::read_request(STATE) {
      file::LockGuard guard(request_fd_, LOCK_EX);

      if(guard.status() != file::eLockSucceeded) {
        logger::error("%s: console: unable to lock request file", strerror(errno));
        return NULL;
      }

      char* buf[1024];

      lseek(request_fd_, 0, SEEK_SET);
      ssize_t bytes = ::read(request_fd_, buf, 1024);

      char* req = NULL;
      if(bytes > 0) {
        req = new char[bytes+1];
        memcpy(req, buf, bytes);
        req[bytes] = 0;
        request_vm_->metrics()->m.console_metrics.requests_received++;
      } else if(bytes < 0) {
        logger::error("%s: console: unable to read request", strerror(errno));
      }

      if(lseek(request_fd_, 0, SEEK_SET) < 0) {
        logger::error("%s: console: unable to rewind request file", strerror(errno));
      }
      if(ftruncate(request_fd_, 0) < 0) {
        logger::error("%s: console: unable to truncate request file", strerror(errno));
      }

      return req;
    }

    void Console::process_requests(STATE) {
      GCTokenImpl gct;
      RBX_DTRACE_CONST char* thread_name =
        const_cast<RBX_DTRACE_CONST char*>("rbx.console.request");
      request_vm_->set_name(thread_name);

      RUBINIUS_THREAD_START(const_cast<RBX_DTRACE_CONST char*>(thread_name),
                            state->vm()->thread_id(), 1);

      state->vm()->thread->hard_unlock(state, gct, 0);
      state->gc_independent(gct, 0);

      while(!request_exit_) {
        Object* status = fsevent_.get()->wait_for_event(state);

        if(request_exit_) break;
        if(status->nil_p()) continue;

        char* request = read_request(state);

        if(request) {
          utilities::thread::Mutex::LockGuard lg(list_lock_);

          request_list_->push_back(request);
          response_cond_.signal();
        }
      }

      RUBINIUS_THREAD_STOP(const_cast<RBX_DTRACE_CONST char*>(thread_name),
                           state->vm()->thread_id(), 1);
    }

    void Console::write_response(STATE, const char* response, native_int size) {
      file::LockGuard guard(response_fd_, LOCK_EX);

      if(guard.status() != file::eLockSucceeded) {
        logger::error("%s: unable to lock response file", strerror(errno));
        return;
      }

      if(lseek(response_fd_, 0, SEEK_SET) < 0) {
        logger::error("%s: console: unable to rewind response file", strerror(errno));
        return;
      }
      if(ftruncate(response_fd_, 0) < 0) {
        logger::error("%s: console: unable to truncate response file", strerror(errno));
        return;
      }

      if(::write(response_fd_, response, size) < 0) {
        logger::error("%s: console: unable to write response", strerror(errno));
      }

      response_vm_->metrics()->m.console_metrics.responses_sent++;
    }

    void Console::process_responses(STATE) {
      GCTokenImpl gct;
      RBX_DTRACE_CONST char* thread_name =
        const_cast<RBX_DTRACE_CONST char*>("rbx.console.response");
      response_vm_->set_name(thread_name);

      RUBINIUS_THREAD_START(const_cast<RBX_DTRACE_CONST char*>(thread_name),
                            state->vm()->thread_id(), 1);

      state->vm()->thread->hard_unlock(state, gct, 0);
      state->gc_dependent(gct, 0);

      char* request = NULL;

      while(!response_exit_) {
        {
          utilities::thread::Mutex::LockGuard lg(list_lock_);
          GCIndependent guard(state, 0);

          if(request_list_->size() > 0) {
            request = request_list_->back();
            request_list_->pop_back();
          }
        }

        if(response_exit_) break;

        if(request) {
          Array* args = Array::create(state, 1);
          args->aset(state, 0, String::create(state, request));
          Object* result = console_.get()->send(state, 0, state->symbol("evaluate"),
              args, cNil);

          if(String* response = try_as<String>(result)) {
            GCIndependent guard(state, 0);

            write_response(state,
                reinterpret_cast<const char*>(response->byte_address()),
                response->byte_size());
          }

          request = NULL;
        } else {
          utilities::thread::Mutex::LockGuard lg(response_lock_);
          GCIndependent guard(state, 0);

          response_cond_.wait(response_lock_);
        }
      }

      RUBINIUS_THREAD_STOP(const_cast<RBX_DTRACE_CONST char*>(thread_name),
                           state->vm()->thread_id(), 1);
    }
  }
}
<|endoftext|>