<commit_before>//
// Created by mwo on 28/05/17.
//

#include "MempoolStatus.h"

#include "rpccalls.h"

namespace xmreg
{

using namespace std;

void
MempoolStatus::set_blockchain_variables(MicroCore *_mcore,
                                        Blockchain *_core_storage)
{
    mcore = _mcore;
    core_storage = _core_storage;
}

void
MempoolStatus::start_mempool_status_thread()
{

    // to protect from deviation by zero below.
    mempool_refresh_time = std::max<uint64_t>(1, mempool_refresh_time);

    if (!is_running)
    {
        m_thread = boost::thread{[]()
        {
         try
         {
             uint64_t loop_index {0};

            // so that network status is checked every minute
            uint64_t loop_index_divider = std::max<uint64_t>(1, 60 / mempool_refresh_time);

            while (true)
            {

             // we just query network status every minute. No sense
             // to do it as frequently as getting mempool data.
             if (loop_index % loop_index_divider == 0)
             {
                 if (!MempoolStatus::read_network_info())
                 {
                     network_info local_copy = current_network_info;

                     cerr << " Cant read network info "<< endl;

                     local_copy.current = false;

                     current_network_info = local_copy;
                 }
                 else
                 {
                     cout << "Current network info read, ";
                     loop_index = 0;
                 }
             }

             if (MempoolStatus::read_mempool())
             {
                 vector<mempool_tx> current_mempool_txs = get_mempool_txs();

                 cout << "mempool status txs: "
                      << current_mempool_txs.size()
                      << endl;
             }

             // when we reach top of the blockchain, update
             // the emission amount every minute.
             boost::this_thread::sleep_for(
                     boost::chrono::seconds(mempool_refresh_time));

             ++loop_index;

             } // while (true)
         }
         catch (boost::thread_interrupted&)
         {
             cout << "Mempool status thread interrupted." << endl;
             return;
        }

        }}; //  m_thread = boost::thread{[]()

        is_running = true;

    } //  if (!is_running)
}

bool
MempoolStatus::read_mempool()
{
    rpccalls rpc {deamon_url};

    string error_msg;

    // we populate this variable instead of global mempool_txs
    // mempool_txs will be changed only when this function completes.
    // this ensures that we don't sent out partial mempool txs to
    // other places.
    vector<mempool_tx> local_copy_of_mempool_txs;

    // get txs in the mempool
    std::vector<tx_info> mempool_tx_info;

    //std::vector<tx_info> pool_tx_info;
    std::vector<spent_key_image_info> pool_key_image_info;

    if (!mcore->get_mempool().get_transactions_and_spent_keys_info(
                mempool_tx_info,
                pool_key_image_info))
    {
        cerr << "Getting mempool failed " << endl;
        return false;
    }

    // if dont have tx_blob member, construct tx
    // from json obtained from the rpc call

    uint64_t mempool_size_kB {0};

    for (size_t i = 0; i < mempool_tx_info.size(); ++i)
    {
        // get transaction info of the tx in the mempool
        const tx_info& _tx_info = mempool_tx_info.at(i);

        transaction tx;
        crypto::hash tx_hash;
        crypto::hash tx_prefix_hash;

        if (!parse_and_validate_tx_from_blob(
                _tx_info.tx_blob, tx, tx_hash, tx_prefix_hash))
        {
            cerr << "Cant make tx from _tx_info.tx_blob" << endl;
            return false;
        }

        mempool_size_kB += _tx_info.blob_size;

        local_copy_of_mempool_txs.push_back(mempool_tx{});

        mempool_tx& last_tx = local_copy_of_mempool_txs.back();

        last_tx.tx_hash = tx_hash;
        last_tx.tx = tx;

        // key images of inputs
        vector<txin_to_key> input_key_imgs;

        // public keys and xmr amount of outputs
        vector<pair<txout_to_key, uint64_t>> output_pub_keys;

        // sum xmr in inputs and ouputs in the given tx
        const array<uint64_t, 4>& sum_data = summary_of_in_out_rct(
               tx, output_pub_keys, input_key_imgs);

        double tx_size =  static_cast<double>(_tx_info.blob_size)/1024.0;

        double payed_for_kB = XMR_AMOUNT(_tx_info.fee) / tx_size;

        last_tx.receive_time = _tx_info.receive_time;

        last_tx.sum_outputs       = sum_data[0];
        last_tx.sum_inputs        = sum_data[1];
        last_tx.no_outputs        = output_pub_keys.size();
        last_tx.no_inputs         = input_key_imgs.size();
        last_tx.mixin_no          = sum_data[2];
        last_tx.num_nonrct_inputs = sum_data[3];

        last_tx.fee_str          = xmreg::xmr_amount_to_str(_tx_info.fee, "{:0.4f}", false);
        last_tx.payed_for_kB_str = fmt::format("{:0.4f}", payed_for_kB);
        last_tx.xmr_inputs_str   = xmreg::xmr_amount_to_str(last_tx.sum_inputs , "{:0.3f}");
        last_tx.xmr_outputs_str  = xmreg::xmr_amount_to_str(last_tx.sum_outputs, "{:0.3f}");
        last_tx.timestamp_str    = xmreg::timestamp_to_str_gm(_tx_info.receive_time);

        last_tx.txsize           = fmt::format("{:0.2f}", tx_size);

        last_tx.pID              = '-';

        crypto::hash payment_id;
        crypto::hash8 payment_id8;

        get_payment_id(tx, payment_id, payment_id8);

        if (payment_id != null_hash)
            last_tx.pID = 'l'; // legacy payment id
        else if (payment_id8 != null_hash8)
            last_tx.pID = 'e'; // encrypted payment id
        else if (!get_additional_tx_pub_keys_from_extra(tx).empty())
        {
            // if multioutput tx have additional public keys,
            // mark it so that it represents that it has at least
            // one sub-address
            last_tx.pID = 's';
        }
       // } // if (hex_to_pod(_tx_info.id_hash, mem_tx_hash))

    } // for (size_t i = 0; i < mempool_tx_info.size(); ++i)

    Guard lck (mempool_mutx);

    // clear current mempool txs vector
    // repopulate it with each execution of read_mempool()
    // not very efficient but good enough for now.

    mempool_no   = local_copy_of_mempool_txs.size();
    mempool_size = mempool_size_kB;

    mempool_txs = std::move(local_copy_of_mempool_txs);

    return true;
}

bool
MempoolStatus::read_network_info()
{
    rpccalls rpc {deamon_url};

    COMMAND_RPC_GET_INFO::response rpc_network_info;

    if (!rpc.get_network_info(rpc_network_info))
        return false;

    uint64_t fee_estimated;

    string error_msg;

    if (!rpc.get_dynamic_per_kb_fee_estimate(
            FEE_ESTIMATE_GRACE_BLOCKS,
            fee_estimated, error_msg))
    {
        cerr << "rpc.get_dynamic_per_kb_fee_estimate failed" << endl;
        return false;
    }

    (void) error_msg;

    COMMAND_RPC_HARD_FORK_INFO::response rpc_hardfork_info;

    if (!rpc.get_hardfork_info(rpc_hardfork_info))
        return false;

    network_info local_copy;

    local_copy.status                     = network_info::get_status_uint(rpc_network_info.status);
    local_copy.height                     = rpc_network_info.height;
    local_copy.target_height              = rpc_network_info.target_height;
    local_copy.difficulty                 = rpc_network_info.difficulty;
    local_copy.target                     = rpc_network_info.target;
    local_copy.hash_rate                  = (rpc_network_info.difficulty/rpc_network_info.target);
    local_copy.tx_count                   = rpc_network_info.tx_count;
    local_copy.tx_pool_size               = rpc_network_info.tx_pool_size;
    local_copy.alt_blocks_count           = rpc_network_info.alt_blocks_count;
    local_copy.outgoing_connections_count = rpc_network_info.outgoing_connections_count;
    local_copy.incoming_connections_count = rpc_network_info.incoming_connections_count;
    local_copy.white_peerlist_size        = rpc_network_info.white_peerlist_size;
    local_copy.nettype                    = rpc_network_info.testnet ? cryptonote::network_type::TESTNET :
                                            rpc_network_info.stagenet ? cryptonote::network_type::STAGENET : cryptonote::network_type::MAINNET;
    local_copy.cumulative_difficulty      = rpc_network_info.cumulative_difficulty;
    local_copy.block_size_limit           = rpc_network_info.block_size_limit;
    local_copy.block_size_median          = rpc_network_info.block_size_median;
    local_copy.block_weight_limit         = rpc_network_info.block_weight_limit;
    local_copy.start_time                 = rpc_network_info.start_time;

    strncpy(local_copy.block_size_limit_str, fmt::format("{:0.2f}",
                                             static_cast<double>(
                                             local_copy.block_size_limit ) / 2.0 / 1024.0).c_str(),
                                             sizeof(local_copy.block_size_limit_str));

    strncpy(local_copy.block_size_median_str, fmt::format("{:0.2f}",
                                              static_cast<double>(
                                              local_copy.block_size_median) / 1024.0).c_str(),
                                              sizeof(local_copy.block_size_median_str));

    epee::string_tools::hex_to_pod(rpc_network_info.top_block_hash,
                                   local_copy.top_block_hash);

    local_copy.fee_per_kb                 = fee_estimated;
    local_copy.info_timestamp             = static_cast<uint64_t>(std::time(nullptr));

    local_copy.current_hf_version         = rpc_hardfork_info.version;

    local_copy.current                    = true;

    current_network_info = local_copy;

    return true;
}

vector<MempoolStatus::mempool_tx>
MempoolStatus::get_mempool_txs()
{
    Guard lck (mempool_mutx);
    return mempool_txs;
}

vector<MempoolStatus::mempool_tx>
MempoolStatus::get_mempool_txs(uint64_t no_of_tx)
{
    Guard lck (mempool_mutx);

    no_of_tx = std::min<uint64_t>(no_of_tx, mempool_txs.size());

    return vector<mempool_tx>(mempool_txs.begin(), mempool_txs.begin() + no_of_tx);
}

bool
MempoolStatus::is_thread_running()
{
    return is_running;
}

bf::path MempoolStatus::blockchain_path {"/home/mwo/.bitmonero/lmdb"};
string MempoolStatus::deamon_url {"http:://127.0.0.1:18081"};
cryptonote::network_type MempoolStatus::nettype {cryptonote::network_type::MAINNET};
atomic<bool>       MempoolStatus::is_running {false};
boost::thread      MempoolStatus::m_thread;
Blockchain*        MempoolStatus::core_storage {nullptr};
xmreg::MicroCore*  MempoolStatus::mcore {nullptr};
vector<MempoolStatus::mempool_tx> MempoolStatus::mempool_txs;
atomic<MempoolStatus::network_info> MempoolStatus::current_network_info;
atomic<uint64_t> MempoolStatus::mempool_no {0};   // no of txs
atomic<uint64_t> MempoolStatus::mempool_size {0}; // size in bytes.
uint64_t MempoolStatus::mempool_refresh_time {10};
mutex MempoolStatus::mempool_mutx;
}
<commit_msg>Fix for txpool: Failed to parse transaction from blob<commit_after>//
// Created by mwo on 28/05/17.
//

#include "MempoolStatus.h"

#include "rpccalls.h"

namespace xmreg
{

using namespace std;

void
MempoolStatus::set_blockchain_variables(MicroCore *_mcore,
                                        Blockchain *_core_storage)
{
    mcore = _mcore;
    core_storage = _core_storage;
}

void
MempoolStatus::start_mempool_status_thread()
{

    // to protect from deviation by zero below.
    mempool_refresh_time = std::max<uint64_t>(1, mempool_refresh_time);

    if (!is_running)
    {
        m_thread = boost::thread{[]()
        {
         try
         {
             uint64_t loop_index {0};

            // so that network status is checked every minute
            uint64_t loop_index_divider = std::max<uint64_t>(1, 60 / mempool_refresh_time);

            while (true)
            {

             // we just query network status every minute. No sense
             // to do it as frequently as getting mempool data.
             if (loop_index % loop_index_divider == 0)
             {
                 if (!MempoolStatus::read_network_info())
                 {
                     network_info local_copy = current_network_info;

                     cerr << " Cant read network info "<< endl;

                     local_copy.current = false;

                     current_network_info = local_copy;
                 }
                 else
                 {
                     cout << "Current network info read, ";
                     loop_index = 0;
                 }
             }

             if (MempoolStatus::read_mempool())
             {
                 vector<mempool_tx> current_mempool_txs = get_mempool_txs();

                 cout << "mempool status txs: "
                      << current_mempool_txs.size()
                      << endl;
             }

             // when we reach top of the blockchain, update
             // the emission amount every minute.
             boost::this_thread::sleep_for(
                     boost::chrono::seconds(mempool_refresh_time));

             ++loop_index;

             } // while (true)
         }
         catch (boost::thread_interrupted&)
         {
             cout << "Mempool status thread interrupted." << endl;
             return;
        }

        }}; //  m_thread = boost::thread{[]()

        is_running = true;

    } //  if (!is_running)
}

bool
MempoolStatus::read_mempool()
{
    rpccalls rpc {deamon_url};

    string error_msg;

    // we populate this variable instead of global mempool_txs
    // mempool_txs will be changed only when this function completes.
    // this ensures that we don't sent out partial mempool txs to
    // other places.
    vector<mempool_tx> local_copy_of_mempool_txs;

    // get txs in the mempool
    std::vector<tx_info> mempool_tx_info;

    //std::vector<tx_info> pool_tx_info;
    std::vector<spent_key_image_info> pool_key_image_info;

    // get txpool from lmdb database instead of rpc call
    if (!mcore->get_mempool().get_transactions_and_spent_keys_info(
                mempool_tx_info,
                pool_key_image_info))
    {
        cerr << "Getting mempool failed " << endl;
        return false;
    }

    (void) pool_key_image_info;

    // if dont have tx_blob member, construct tx
    // from json obtained from the rpc call

    uint64_t mempool_size_kB {0};

    for (size_t i = 0; i < mempool_tx_info.size(); ++i)
    {
        // get transaction info of the tx in the mempool
        const tx_info& _tx_info = mempool_tx_info.at(i);

        transaction tx;
        crypto::hash tx_hash;
        crypto::hash tx_prefix_hash;

        if (!parse_and_validate_tx_from_blob(
                _tx_info.tx_blob, tx, tx_hash, tx_prefix_hash))
        {
            cerr << "Cant make tx from _tx_info.tx_blob" << endl;
            return false;
        }

        mempool_size_kB += _tx_info.blob_size;

        local_copy_of_mempool_txs.push_back(mempool_tx{});

        mempool_tx& last_tx = local_copy_of_mempool_txs.back();

        last_tx.tx_hash = tx_hash;
        last_tx.tx = tx;

        // key images of inputs
        vector<txin_to_key> input_key_imgs;

        // public keys and xmr amount of outputs
        vector<pair<txout_to_key, uint64_t>> output_pub_keys;

        // sum xmr in inputs and ouputs in the given tx
        const array<uint64_t, 4>& sum_data = summary_of_in_out_rct(
               tx, output_pub_keys, input_key_imgs);

        double tx_size =  static_cast<double>(_tx_info.blob_size)/1024.0;

        double payed_for_kB = XMR_AMOUNT(_tx_info.fee) / tx_size;

        last_tx.receive_time = _tx_info.receive_time;

        last_tx.sum_outputs       = sum_data[0];
        last_tx.sum_inputs        = sum_data[1];
        last_tx.no_outputs        = output_pub_keys.size();
        last_tx.no_inputs         = input_key_imgs.size();
        last_tx.mixin_no          = sum_data[2];
        last_tx.num_nonrct_inputs = sum_data[3];

        last_tx.fee_str          = xmreg::xmr_amount_to_str(_tx_info.fee, "{:0.4f}", false);
        last_tx.payed_for_kB_str = fmt::format("{:0.4f}", payed_for_kB);
        last_tx.xmr_inputs_str   = xmreg::xmr_amount_to_str(last_tx.sum_inputs , "{:0.3f}");
        last_tx.xmr_outputs_str  = xmreg::xmr_amount_to_str(last_tx.sum_outputs, "{:0.3f}");
        last_tx.timestamp_str    = xmreg::timestamp_to_str_gm(_tx_info.receive_time);

        last_tx.txsize           = fmt::format("{:0.2f}", tx_size);

        last_tx.pID              = '-';

        crypto::hash payment_id;
        crypto::hash8 payment_id8;

        get_payment_id(tx, payment_id, payment_id8);

        if (payment_id != null_hash)
            last_tx.pID = 'l'; // legacy payment id
        else if (payment_id8 != null_hash8)
            last_tx.pID = 'e'; // encrypted payment id
        else if (!get_additional_tx_pub_keys_from_extra(tx).empty())
        {
            // if multioutput tx have additional public keys,
            // mark it so that it represents that it has at least
            // one sub-address
            last_tx.pID = 's';
        }
       // } // if (hex_to_pod(_tx_info.id_hash, mem_tx_hash))

    } // for (size_t i = 0; i < mempool_tx_info.size(); ++i)

    Guard lck (mempool_mutx);

    // clear current mempool txs vector
    // repopulate it with each execution of read_mempool()
    // not very efficient but good enough for now.

    mempool_no   = local_copy_of_mempool_txs.size();
    mempool_size = mempool_size_kB;

    mempool_txs = std::move(local_copy_of_mempool_txs);

    return true;
}

bool
MempoolStatus::read_network_info()
{
    rpccalls rpc {deamon_url};

    COMMAND_RPC_GET_INFO::response rpc_network_info;

    if (!rpc.get_network_info(rpc_network_info))
        return false;

    uint64_t fee_estimated;

    string error_msg;

    if (!rpc.get_dynamic_per_kb_fee_estimate(
            FEE_ESTIMATE_GRACE_BLOCKS,
            fee_estimated, error_msg))
    {
        cerr << "rpc.get_dynamic_per_kb_fee_estimate failed" << endl;
        return false;
    }

    (void) error_msg;

    COMMAND_RPC_HARD_FORK_INFO::response rpc_hardfork_info;

    if (!rpc.get_hardfork_info(rpc_hardfork_info))
        return false;

    network_info local_copy;

    local_copy.status                     = network_info::get_status_uint(rpc_network_info.status);
    local_copy.height                     = rpc_network_info.height;
    local_copy.target_height              = rpc_network_info.target_height;
    local_copy.difficulty                 = rpc_network_info.difficulty;
    local_copy.target                     = rpc_network_info.target;
    local_copy.hash_rate                  = (rpc_network_info.difficulty/rpc_network_info.target);
    local_copy.tx_count                   = rpc_network_info.tx_count;
    local_copy.tx_pool_size               = rpc_network_info.tx_pool_size;
    local_copy.alt_blocks_count           = rpc_network_info.alt_blocks_count;
    local_copy.outgoing_connections_count = rpc_network_info.outgoing_connections_count;
    local_copy.incoming_connections_count = rpc_network_info.incoming_connections_count;
    local_copy.white_peerlist_size        = rpc_network_info.white_peerlist_size;
    local_copy.nettype                    = rpc_network_info.testnet ? cryptonote::network_type::TESTNET :
                                            rpc_network_info.stagenet ? cryptonote::network_type::STAGENET : cryptonote::network_type::MAINNET;
    local_copy.cumulative_difficulty      = rpc_network_info.cumulative_difficulty;
    local_copy.block_size_limit           = rpc_network_info.block_size_limit;
    local_copy.block_size_median          = rpc_network_info.block_size_median;
    local_copy.block_weight_limit         = rpc_network_info.block_weight_limit;
    local_copy.start_time                 = rpc_network_info.start_time;

    strncpy(local_copy.block_size_limit_str, fmt::format("{:0.2f}",
                                             static_cast<double>(
                                             local_copy.block_size_limit ) / 2.0 / 1024.0).c_str(),
                                             sizeof(local_copy.block_size_limit_str));

    strncpy(local_copy.block_size_median_str, fmt::format("{:0.2f}",
                                              static_cast<double>(
                                              local_copy.block_size_median) / 1024.0).c_str(),
                                              sizeof(local_copy.block_size_median_str));

    epee::string_tools::hex_to_pod(rpc_network_info.top_block_hash,
                                   local_copy.top_block_hash);

    local_copy.fee_per_kb                 = fee_estimated;
    local_copy.info_timestamp             = static_cast<uint64_t>(std::time(nullptr));

    local_copy.current_hf_version         = rpc_hardfork_info.version;

    local_copy.current                    = true;

    current_network_info = local_copy;

    return true;
}

vector<MempoolStatus::mempool_tx>
MempoolStatus::get_mempool_txs()
{
    Guard lck (mempool_mutx);
    return mempool_txs;
}

vector<MempoolStatus::mempool_tx>
MempoolStatus::get_mempool_txs(uint64_t no_of_tx)
{
    Guard lck (mempool_mutx);

    no_of_tx = std::min<uint64_t>(no_of_tx, mempool_txs.size());

    return vector<mempool_tx>(mempool_txs.begin(), mempool_txs.begin() + no_of_tx);
}

bool
MempoolStatus::is_thread_running()
{
    return is_running;
}

bf::path MempoolStatus::blockchain_path {"/home/mwo/.bitmonero/lmdb"};
string MempoolStatus::deamon_url {"http:://127.0.0.1:18081"};
cryptonote::network_type MempoolStatus::nettype {cryptonote::network_type::MAINNET};
atomic<bool>       MempoolStatus::is_running {false};
boost::thread      MempoolStatus::m_thread;
Blockchain*        MempoolStatus::core_storage {nullptr};
xmreg::MicroCore*  MempoolStatus::mcore {nullptr};
vector<MempoolStatus::mempool_tx> MempoolStatus::mempool_txs;
atomic<MempoolStatus::network_info> MempoolStatus::current_network_info;
atomic<uint64_t> MempoolStatus::mempool_no {0};   // no of txs
atomic<uint64_t> MempoolStatus::mempool_size {0}; // size in bytes.
uint64_t MempoolStatus::mempool_refresh_time {10};
mutex MempoolStatus::mempool_mutx;
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2017 ScyllaDB
 */

/*
 * This file is part of Scylla.
 *
 * Scylla is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Scylla is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Scylla.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <boost/test/unit_test.hpp>
#include "utils/loading_shared_values.hh"
#include "utils/loading_cache.hh"
#include <seastar/core/file.hh>
#include <seastar/core/thread.hh>
#include <seastar/core/sstring.hh>
#include <seastar/core/reactor.hh>
#include <seastar/core/sleep.hh>

#include "seastarx.hh"

#include "tests/test-utils.hh"
#include "tmpdir.hh"
#include "log.hh"

#include <vector>
#include <numeric>
#include <random>

/// Get a random integer in the [0, max) range.
/// \param upper bound of the random value range
/// \return The uniformly distributed random integer from the [0, \ref max) range.
static int rand_int(int max) {
    std::random_device rd;     // only used once to initialise (seed) engine
    std::mt19937 rng(rd());    // random-number engine used (Mersenne-Twister in this case)
    std::uniform_int_distribution<int> uni(0, max - 1); // guaranteed unbiased
    return uni(rng);
}

#include "disk-error-handler.hh"

thread_local disk_error_signal_type general_disk_error;
thread_local disk_error_signal_type commit_error;

static const sstring test_file_name = "loading_cache_test.txt";
static const sstring test_string = "1";
static bool file_prepared = false;
static constexpr int num_loaders = 1000;

static logging::logger test_logger("loading_cache_test");

static thread_local int load_count;
static const tmpdir& get_tmpdir() {
    static thread_local tmpdir tmp;
    return tmp;
}

static future<> prepare() {
    if (file_prepared) {
        return make_ready_future<>();
    }

    return open_file_dma((boost::filesystem::path(get_tmpdir().path) / test_file_name.c_str()).c_str(), open_flags::create | open_flags::wo).then([] (file f) {
        return do_with(std::move(f), [] (file& f) {
            return f.dma_write(0, test_string.c_str(), test_string.size() + 1).then([] (size_t s) {
                BOOST_REQUIRE_EQUAL(s, test_string.size() + 1);
                file_prepared = true;
            });
        });
    });
}

static future<sstring> loader(const int& k) {
    return open_file_dma((boost::filesystem::path(get_tmpdir().path) / test_file_name.c_str()).c_str(), open_flags::ro).then([] (file f) -> future<sstring> {
        return do_with(std::move(f), [] (file& f) -> future<sstring> {
            return f.dma_read_exactly<char>(0, test_string.size() + 1).then([] (auto buf) {
                sstring str(buf.get());
                BOOST_REQUIRE_EQUAL(str, test_string);
                ++load_count;
                return make_ready_future<sstring>(std::move(str));
            });
        });
    });
}

SEASTAR_TEST_CASE(test_loading_shared_values_parallel_loading_same_key) {
    return seastar::async([] {
        std::vector<int> ivec(num_loaders);
        load_count = 0;
        utils::loading_shared_values<int, sstring> shared_values;
        std::list<typename utils::loading_shared_values<int, sstring>::entry_ptr> anchors_list;

        prepare().get();

        std::fill(ivec.begin(), ivec.end(), 0);

        parallel_for_each(ivec, [&] (int& k) {
            return shared_values.get_or_load(k, loader).then([&] (auto entry_ptr) {
                anchors_list.emplace_back(std::move(entry_ptr));
            });
        }).get();

        // "loader" must be called exactly once
        BOOST_REQUIRE_EQUAL(load_count, 1);
        BOOST_REQUIRE_EQUAL(shared_values.size(), 1);
        anchors_list.clear();
    });
}

SEASTAR_TEST_CASE(test_loading_shared_values_parallel_loading_different_keys) {
    return seastar::async([] {
        std::vector<int> ivec(num_loaders);
        load_count = 0;
        utils::loading_shared_values<int, sstring> shared_values;
        std::list<typename utils::loading_shared_values<int, sstring>::entry_ptr> anchors_list;

        prepare().get();

        std::iota(ivec.begin(), ivec.end(), 0);

        parallel_for_each(ivec, [&] (int& k) {
            return shared_values.get_or_load(k, loader).then([&] (auto entry_ptr) {
                anchors_list.emplace_back(std::move(entry_ptr));
            });
        }).get();

        // "loader" must be called once for each key
        BOOST_REQUIRE_EQUAL(load_count, num_loaders);
        BOOST_REQUIRE_EQUAL(shared_values.size(), num_loaders);
        anchors_list.clear();
    });
}

SEASTAR_TEST_CASE(test_loading_shared_values_rehash) {
    return seastar::async([] {
        std::vector<int> ivec(num_loaders);
        load_count = 0;
        utils::loading_shared_values<int, sstring> shared_values;
        std::list<typename utils::loading_shared_values<int, sstring>::entry_ptr> anchors_list;

        prepare().get();

        std::iota(ivec.begin(), ivec.end(), 0);

        // verify that load factor is always in the (0.25, 0.75) range
        for (int k = 0; k < num_loaders; ++k) {
            shared_values.get_or_load(k, loader).then([&] (auto entry_ptr) {
                anchors_list.emplace_back(std::move(entry_ptr));
            }).get();
            BOOST_REQUIRE_LE(shared_values.size(), 3 * shared_values.buckets_count() / 4);
        }

        BOOST_REQUIRE_GE(shared_values.size(), shared_values.buckets_count() / 4);

        // minimum buckets count (by default) is 16, so don't check for less than 4 elements
        for (int k = 0; k < num_loaders - 4; ++k) {
            anchors_list.pop_back();
            shared_values.rehash();
            BOOST_REQUIRE_GE(shared_values.size(), shared_values.buckets_count() / 4);
        }

        anchors_list.clear();
    });
}

SEASTAR_TEST_CASE(test_loading_shared_values_parallel_loading_explicit_eviction) {
    return seastar::async([] {
        std::vector<int> ivec(num_loaders);
        load_count = 0;
        utils::loading_shared_values<int, sstring> shared_values;
        std::vector<typename utils::loading_shared_values<int, sstring>::entry_ptr> anchors_vec(num_loaders);

        prepare().get();

        std::iota(ivec.begin(), ivec.end(), 0);

        parallel_for_each(ivec, [&] (int& k) {
            return shared_values.get_or_load(k, loader).then([&] (auto entry_ptr) {
                anchors_vec[k] = std::move(entry_ptr);
            });
        }).get();

        int rand_key = rand_int(num_loaders);
        BOOST_REQUIRE(shared_values.find(rand_key) != shared_values.end());
        anchors_vec[rand_key] = nullptr;
        BOOST_REQUIRE_MESSAGE(shared_values.find(rand_key) == shared_values.end(), format("explicit removal for key {} failed", rand_key));
        anchors_vec.clear();
    });
}

SEASTAR_TEST_CASE(test_loading_cache_loading_same_key) {
    return seastar::async([] {
        using namespace std::chrono;
        std::vector<int> ivec(num_loaders);
        load_count = 0;
        utils::loading_cache<int, sstring> loading_cache(num_loaders, 1s, test_logger);

        prepare().get();

        std::fill(ivec.begin(), ivec.end(), 0);

        parallel_for_each(ivec, [&] (int& k) {
            return loading_cache.get_ptr(k, loader).discard_result();
        }).get();

        // "loader" must be called exactly once
        BOOST_REQUIRE_EQUAL(load_count, 1);
        BOOST_REQUIRE_EQUAL(loading_cache.size(), 1);
        loading_cache.stop().get();
    });
}

SEASTAR_TEST_CASE(test_loading_cache_loading_different_keys) {
    return seastar::async([] {
        using namespace std::chrono;
        std::vector<int> ivec(num_loaders);
        load_count = 0;
        utils::loading_cache<int, sstring> loading_cache(num_loaders, 1s, test_logger);

        prepare().get();

        std::iota(ivec.begin(), ivec.end(), 0);

        parallel_for_each(ivec, [&] (int& k) {
            return loading_cache.get_ptr(k, loader).discard_result();
        }).get();

        BOOST_REQUIRE_EQUAL(load_count, num_loaders);
        BOOST_REQUIRE_EQUAL(loading_cache.size(), num_loaders);
        loading_cache.stop().get();
    });
}

SEASTAR_TEST_CASE(test_loading_cache_loading_expiry_eviction) {
    return seastar::async([] {
        using namespace std::chrono;
        utils::loading_cache<int, sstring> loading_cache(num_loaders, 20ms, test_logger);

        prepare().get();

        loading_cache.get_ptr(0, loader).discard_result().get();

        BOOST_REQUIRE(loading_cache.find(0) != loading_cache.end());

        // timers get delayed sometimes (especially in a debug mode)
        constexpr int max_retry = 10;
        int i = 0;
        do_until(
            [&] { return i++ > max_retry || loading_cache.find(0) == loading_cache.end(); },
            [] { return sleep(40ms); }
        ).get();
        BOOST_REQUIRE(loading_cache.find(0) == loading_cache.end());
        loading_cache.stop().get();
    });
}

SEASTAR_TEST_CASE(test_loading_cache_loading_reloading) {
    return seastar::async([] {
        using namespace std::chrono;
        load_count = 0;
        utils::loading_cache<int, sstring, utils::loading_cache_reload_enabled::yes> loading_cache(num_loaders, 100ms, 20ms, test_logger, loader);
        prepare().get();
        loading_cache.get_ptr(0, loader).discard_result().get();
        sleep(60ms).get();
        BOOST_REQUIRE_MESSAGE(load_count >= 2, format("load_count is {}", load_count));
        loading_cache.stop().get();
    });
}

SEASTAR_TEST_CASE(test_loading_cache_max_size_eviction) {
    return seastar::async([] {
        using namespace std::chrono;
        load_count = 0;
        utils::loading_cache<int, sstring> loading_cache(1, 1s, test_logger);

        prepare().get();

        for (int i = 0; i < num_loaders; ++i) {
            loading_cache.get_ptr(i % 2, loader).discard_result().get();
        }

        BOOST_REQUIRE_EQUAL(load_count, num_loaders);
        BOOST_REQUIRE_EQUAL(loading_cache.size(), 1);
        loading_cache.stop().get();
    });
}

SEASTAR_TEST_CASE(test_loading_cache_reload_during_eviction) {
    return seastar::async([] {
        using namespace std::chrono;
        load_count = 0;
        utils::loading_cache<int, sstring, utils::loading_cache_reload_enabled::yes> loading_cache(1, 100ms, 10ms, test_logger, loader);

        prepare().get();

        auto curr_time = lowres_clock::now();
        int i = 0;

        // this will cause reloading when values are being actively evicted due to the limited cache size
        do_until(
            [&] { return lowres_clock::now() - curr_time > 1s; },
            [&] { return loading_cache.get_ptr(i++ % 2).discard_result(); }
        ).get();

        BOOST_REQUIRE_EQUAL(loading_cache.size(), 1);
        loading_cache.stop().get();
    });
}<commit_msg>tests: loading_cache_test: make it more robust<commit_after>/*
 * Copyright (C) 2017 ScyllaDB
 */

/*
 * This file is part of Scylla.
 *
 * Scylla is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Scylla is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Scylla.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <boost/test/unit_test.hpp>
#include "utils/loading_shared_values.hh"
#include "utils/loading_cache.hh"
#include <seastar/core/file.hh>
#include <seastar/core/thread.hh>
#include <seastar/core/sstring.hh>
#include <seastar/core/reactor.hh>
#include <seastar/core/sleep.hh>
#include <seastar/util/defer.hh>

#include "seastarx.hh"

#include "tests/test-utils.hh"
#include "tmpdir.hh"
#include "log.hh"

#include <vector>
#include <numeric>
#include <random>

/// Get a random integer in the [0, max) range.
/// \param upper bound of the random value range
/// \return The uniformly distributed random integer from the [0, \ref max) range.
static int rand_int(int max) {
    std::random_device rd;     // only used once to initialise (seed) engine
    std::mt19937 rng(rd());    // random-number engine used (Mersenne-Twister in this case)
    std::uniform_int_distribution<int> uni(0, max - 1); // guaranteed unbiased
    return uni(rng);
}

#include "disk-error-handler.hh"

thread_local disk_error_signal_type general_disk_error;
thread_local disk_error_signal_type commit_error;

static const sstring test_file_name = "loading_cache_test.txt";
static const sstring test_string = "1";
static bool file_prepared = false;
static constexpr int num_loaders = 1000;

static logging::logger test_logger("loading_cache_test");

static thread_local int load_count;
static const tmpdir& get_tmpdir() {
    static thread_local tmpdir tmp;
    return tmp;
}

static future<> prepare() {
    if (file_prepared) {
        return make_ready_future<>();
    }

    return open_file_dma((boost::filesystem::path(get_tmpdir().path) / test_file_name.c_str()).c_str(), open_flags::create | open_flags::wo).then([] (file f) {
        return do_with(std::move(f), [] (file& f) {
            return f.dma_write(0, test_string.c_str(), test_string.size() + 1).then([] (size_t s) {
                BOOST_REQUIRE_EQUAL(s, test_string.size() + 1);
                file_prepared = true;
            });
        });
    });
}

static future<sstring> loader(const int& k) {
    return open_file_dma((boost::filesystem::path(get_tmpdir().path) / test_file_name.c_str()).c_str(), open_flags::ro).then([] (file f) -> future<sstring> {
        return do_with(std::move(f), [] (file& f) -> future<sstring> {
            return f.dma_read_exactly<char>(0, test_string.size() + 1).then([] (auto buf) {
                sstring str(buf.get());
                BOOST_REQUIRE_EQUAL(str, test_string);
                ++load_count;
                return make_ready_future<sstring>(std::move(str));
            });
        });
    });
}

SEASTAR_TEST_CASE(test_loading_shared_values_parallel_loading_same_key) {
    return seastar::async([] {
        std::vector<int> ivec(num_loaders);
        load_count = 0;
        utils::loading_shared_values<int, sstring> shared_values;
        std::list<typename utils::loading_shared_values<int, sstring>::entry_ptr> anchors_list;

        prepare().get();

        std::fill(ivec.begin(), ivec.end(), 0);

        parallel_for_each(ivec, [&] (int& k) {
            return shared_values.get_or_load(k, loader).then([&] (auto entry_ptr) {
                anchors_list.emplace_back(std::move(entry_ptr));
            });
        }).get();

        // "loader" must be called exactly once
        BOOST_REQUIRE_EQUAL(load_count, 1);
        BOOST_REQUIRE_EQUAL(shared_values.size(), 1);
        anchors_list.clear();
    });
}

SEASTAR_TEST_CASE(test_loading_shared_values_parallel_loading_different_keys) {
    return seastar::async([] {
        std::vector<int> ivec(num_loaders);
        load_count = 0;
        utils::loading_shared_values<int, sstring> shared_values;
        std::list<typename utils::loading_shared_values<int, sstring>::entry_ptr> anchors_list;

        prepare().get();

        std::iota(ivec.begin(), ivec.end(), 0);

        parallel_for_each(ivec, [&] (int& k) {
            return shared_values.get_or_load(k, loader).then([&] (auto entry_ptr) {
                anchors_list.emplace_back(std::move(entry_ptr));
            });
        }).get();

        // "loader" must be called once for each key
        BOOST_REQUIRE_EQUAL(load_count, num_loaders);
        BOOST_REQUIRE_EQUAL(shared_values.size(), num_loaders);
        anchors_list.clear();
    });
}

SEASTAR_TEST_CASE(test_loading_shared_values_rehash) {
    return seastar::async([] {
        std::vector<int> ivec(num_loaders);
        load_count = 0;
        utils::loading_shared_values<int, sstring> shared_values;
        std::list<typename utils::loading_shared_values<int, sstring>::entry_ptr> anchors_list;

        prepare().get();

        std::iota(ivec.begin(), ivec.end(), 0);

        // verify that load factor is always in the (0.25, 0.75) range
        for (int k = 0; k < num_loaders; ++k) {
            shared_values.get_or_load(k, loader).then([&] (auto entry_ptr) {
                anchors_list.emplace_back(std::move(entry_ptr));
            }).get();
            BOOST_REQUIRE_LE(shared_values.size(), 3 * shared_values.buckets_count() / 4);
        }

        BOOST_REQUIRE_GE(shared_values.size(), shared_values.buckets_count() / 4);

        // minimum buckets count (by default) is 16, so don't check for less than 4 elements
        for (int k = 0; k < num_loaders - 4; ++k) {
            anchors_list.pop_back();
            shared_values.rehash();
            BOOST_REQUIRE_GE(shared_values.size(), shared_values.buckets_count() / 4);
        }

        anchors_list.clear();
    });
}

SEASTAR_TEST_CASE(test_loading_shared_values_parallel_loading_explicit_eviction) {
    return seastar::async([] {
        std::vector<int> ivec(num_loaders);
        load_count = 0;
        utils::loading_shared_values<int, sstring> shared_values;
        std::vector<typename utils::loading_shared_values<int, sstring>::entry_ptr> anchors_vec(num_loaders);

        prepare().get();

        std::iota(ivec.begin(), ivec.end(), 0);

        parallel_for_each(ivec, [&] (int& k) {
            return shared_values.get_or_load(k, loader).then([&] (auto entry_ptr) {
                anchors_vec[k] = std::move(entry_ptr);
            });
        }).get();

        int rand_key = rand_int(num_loaders);
        BOOST_REQUIRE(shared_values.find(rand_key) != shared_values.end());
        anchors_vec[rand_key] = nullptr;
        BOOST_REQUIRE_MESSAGE(shared_values.find(rand_key) == shared_values.end(), format("explicit removal for key {} failed", rand_key));
        anchors_vec.clear();
    });
}

SEASTAR_TEST_CASE(test_loading_cache_loading_same_key) {
    return seastar::async([] {
        using namespace std::chrono;
        std::vector<int> ivec(num_loaders);
        load_count = 0;
        utils::loading_cache<int, sstring> loading_cache(num_loaders, 1s, test_logger);
        auto stop_cache_reload = seastar::defer([&loading_cache] { loading_cache.stop().get(); });

        prepare().get();

        std::fill(ivec.begin(), ivec.end(), 0);

        parallel_for_each(ivec, [&] (int& k) {
            return loading_cache.get_ptr(k, loader).discard_result();
        }).get();

        // "loader" must be called exactly once
        BOOST_REQUIRE_EQUAL(load_count, 1);
        BOOST_REQUIRE_EQUAL(loading_cache.size(), 1);
    });
}

SEASTAR_TEST_CASE(test_loading_cache_loading_different_keys) {
    return seastar::async([] {
        using namespace std::chrono;
        std::vector<int> ivec(num_loaders);
        load_count = 0;
        utils::loading_cache<int, sstring> loading_cache(num_loaders, 1s, test_logger);
        auto stop_cache_reload = seastar::defer([&loading_cache] { loading_cache.stop().get(); });

        prepare().get();

        std::iota(ivec.begin(), ivec.end(), 0);

        parallel_for_each(ivec, [&] (int& k) {
            return loading_cache.get_ptr(k, loader).discard_result();
        }).get();

        BOOST_REQUIRE_EQUAL(load_count, num_loaders);
        BOOST_REQUIRE_EQUAL(loading_cache.size(), num_loaders);
    });
}

SEASTAR_TEST_CASE(test_loading_cache_loading_expiry_eviction) {
    return seastar::async([] {
        using namespace std::chrono;
        utils::loading_cache<int, sstring> loading_cache(num_loaders, 20ms, test_logger);
        auto stop_cache_reload = seastar::defer([&loading_cache] { loading_cache.stop().get(); });

        prepare().get();

        loading_cache.get_ptr(0, loader).discard_result().get();

        BOOST_REQUIRE(loading_cache.find(0) != loading_cache.end());

        // timers get delayed sometimes (especially in a debug mode)
        constexpr int max_retry = 10;
        int i = 0;
        do_until(
            [&] { return i++ > max_retry || loading_cache.find(0) == loading_cache.end(); },
            [] { return sleep(40ms); }
        ).get();
        BOOST_REQUIRE(loading_cache.find(0) == loading_cache.end());
    });
}

SEASTAR_TEST_CASE(test_loading_cache_loading_reloading) {
    return seastar::async([] {
        using namespace std::chrono;
        load_count = 0;
        utils::loading_cache<int, sstring, utils::loading_cache_reload_enabled::yes> loading_cache(num_loaders, 100ms, 20ms, test_logger, loader);
        auto stop_cache_reload = seastar::defer([&loading_cache] { loading_cache.stop().get(); });
        prepare().get();
        loading_cache.get_ptr(0, loader).discard_result().get();
        sleep(60ms).get();
        BOOST_REQUIRE_MESSAGE(load_count >= 2, format("load_count is {}", load_count));
    });
}

SEASTAR_TEST_CASE(test_loading_cache_max_size_eviction) {
    return seastar::async([] {
        using namespace std::chrono;
        load_count = 0;
        utils::loading_cache<int, sstring> loading_cache(1, 1s, test_logger);
        auto stop_cache_reload = seastar::defer([&loading_cache] { loading_cache.stop().get(); });

        prepare().get();

        for (int i = 0; i < num_loaders; ++i) {
            loading_cache.get_ptr(i % 2, loader).discard_result().get();
        }

        BOOST_REQUIRE_EQUAL(load_count, num_loaders);
        BOOST_REQUIRE_EQUAL(loading_cache.size(), 1);
    });
}

SEASTAR_TEST_CASE(test_loading_cache_reload_during_eviction) {
    return seastar::async([] {
        using namespace std::chrono;
        load_count = 0;
        utils::loading_cache<int, sstring, utils::loading_cache_reload_enabled::yes> loading_cache(1, 100ms, 10ms, test_logger, loader);
        auto stop_cache_reload = seastar::defer([&loading_cache] { loading_cache.stop().get(); });

        prepare().get();

        auto curr_time = lowres_clock::now();
        int i = 0;

        // this will cause reloading when values are being actively evicted due to the limited cache size
        do_until(
            [&] { return lowres_clock::now() - curr_time > 1s; },
            [&] { return loading_cache.get_ptr(i++ % 2).discard_result(); }
        ).get();

        BOOST_REQUIRE_EQUAL(loading_cache.size(), 1);
    });
}<|endoftext|>


<commit_before>/***************************************************************************
                          msxml.cpp  -  XML Helper
                             -------------------
    begin                : sam mai 17 2003
    copyright            : (C) 2003 by Michael CATANZARITI
    email                : mcatan@free.fr
 ***************************************************************************/

/***************************************************************************
* Copyright (C) The Apache Software Foundation. All rights reserved.      *
*                                                                         *
* This software is published under the terms of the Apache Software       *
* License version 1.1, a copy of which has been included with this        *
* distribution in the LICENSE.txt file.                                   *
***************************************************************************/

#include <log4cxx/config.h>

#ifdef HAVE_MS_XML

#include <log4cxx/helpers/msxml.h>
#include <log4cxx/helpers/loglog.h>

using namespace log4cxx;
using namespace log4cxx::helpers;

IMPLEMENT_LOG4CXX_OBJECT(MsXMLDOMDocument)
IMPLEMENT_LOG4CXX_OBJECT(MsXMLDOMNodeList)
IMPLEMENT_LOG4CXX_OBJECT(MsXMLDOMNode)
IMPLEMENT_LOG4CXX_OBJECT(MsXMLDOMElement)

#define EXEC(stmt) { HRESULT hr = stmt; if (FAILED(hr)) throw DOMException(); }

// MsXMLDOMNode

MsXMLDOMNode::MsXMLDOMNode(MSXML::IXMLDOMNodePtr node)
: node(node)
{
}

XMLDOMNodeListPtr MsXMLDOMNode::getChildNodes()
{
	MSXML::IXMLDOMNodeListPtr nodeList;
	EXEC(node->get_childNodes(&nodeList));
	return new MsXMLDOMNodeList(nodeList);
}

XMLDOMDocumentPtr MsXMLDOMNode::getOwnerDocument()
{
	MSXML::IXMLDOMDocumentPtr document;
	EXEC(node->get_ownerDocument(&document));
	return new MsXMLDOMDocument(document);
}

// MsXMLDOMDocument

MsXMLDOMDocument::MsXMLDOMDocument(MSXML::IXMLDOMDocumentPtr document)
: document(document)
{
}

MsXMLDOMDocument::MsXMLDOMDocument()
{
	HRESULT hRes;
	hRes = document.CreateInstance(L"Msxml2.DOMDocument.3.0");
	if (FAILED(hRes))
	{
		hRes = document.CreateInstance(L"Msxml2.DOMDocument.2.6");
		if (FAILED(hRes))
		{
			hRes = document.CreateInstance(L"Msxml2.DOMDocument");
			if (FAILED(hRes))
			{
				hRes = document.CreateInstance(L"Msxml.DOMDocument");
				if (FAILED(hRes))
				{
					throw DOMException();
				}
			}
		}
	}
}

XMLDOMNodeListPtr MsXMLDOMDocument::getChildNodes()
{
	MSXML::IXMLDOMNodeListPtr nodeList;
	EXEC(document->get_childNodes(&nodeList));
	return new MsXMLDOMNodeList(nodeList);
}

XMLDOMDocumentPtr MsXMLDOMDocument::getOwnerDocument()
{
	return this;
}

void MsXMLDOMDocument::load(const String& fileName)
{
	try
	{
		VARIANT_BOOL bSuccess = document->load(fileName.c_str());

		if (!bSuccess)
		{
			MSXML::IXMLDOMParseErrorPtr parseError = document->parseError;

			// fetch errorcode
			long errorCode = parseError->errorCode;

			// XML file not found
			if (errorCode == INET_E_RESOURCE_NOT_FOUND)
			{
				LogLog::error(_T("Could not find [")+fileName+_T("]."));
			}
			else
			{
				_bstr_t reason = parseError->reason;
				long line = parseError->line;
				long linepos = parseError->linepos;

				// remove \n or \r
				int len = reason.length();
				while(len > 0 && (((BSTR)reason)[len -1] == L'\n' ||
					((BSTR)reason)[len -1] == L'\r'))
				{
					((BSTR)reason)[len -1] = L'\0';
					len--;
				}

				USES_CONVERSION;
				LOGLOG_ERROR(_T("Could not open [") << fileName << _T("] : ")
					<< W2T((BSTR)reason) << _T("(line ") << line << _T(", column ")
					<< linepos << _T(")"));
			}
		}

	}
	catch(_com_error&)
	{
		LogLog::error(_T("Could not open [")+fileName+_T("]."));
		throw DOMException();
	}
}

XMLDOMElementPtr MsXMLDOMDocument::getDocumentElement()
{
	MSXML::IXMLDOMElementPtr element;
	EXEC(document->get_documentElement(&element));
	return new MsXMLDOMElement(element);
}

XMLDOMElementPtr MsXMLDOMDocument::getElementById(const String& tagName, const String& elementId)
{
	MSXML::IXMLDOMElementPtr element;

	try
	{
		MSXML::IXMLDOMNodeListPtr list = document->getElementsByTagName(tagName.c_str());
		for (int t=0; t < list->length; t++)
		{
			MSXML::IXMLDOMNodePtr node = list->item[t];
			MSXML::IXMLDOMNamedNodeMapPtr map= node->attributes;
			MSXML::IXMLDOMNodePtr attrNode = map->getNamedItem(L"name");
			_bstr_t nodeValue = attrNode->nodeValue;

			USES_CONVERSION;
			if (elementId == W2T((BSTR)nodeValue))
			{
				element = node;
				break;
			}
		}
	}
	catch(_com_error&)
	{
		throw DOMException();
	}

	return new MsXMLDOMElement(element);
}

// MsXMLDOMElement
MsXMLDOMElement::MsXMLDOMElement(MSXML::IXMLDOMElementPtr element)
: element(element)
{
}

XMLDOMNodeListPtr MsXMLDOMElement::getChildNodes()
{
	MSXML::IXMLDOMNodeListPtr nodeList;
	EXEC(element->get_childNodes(&nodeList));
	return new MsXMLDOMNodeList(nodeList);
}

XMLDOMDocumentPtr MsXMLDOMElement::getOwnerDocument()
{
	MSXML::IXMLDOMDocumentPtr document;
	EXEC(element->get_ownerDocument(&document));
	return new MsXMLDOMDocument(document);
}

String MsXMLDOMElement::getTagName()
{
	try
	{
		_bstr_t tagName = element->tagName;
		USES_CONVERSION;
		return W2T((BSTR)tagName);
	}
	catch(_com_error&)
	{
		throw DOMException();
	}
}

String MsXMLDOMElement::getAttribute(const String& name)
{
	try
	{
		_variant_t attribute = element->getAttribute(name.c_str());
		if (attribute.vt == VT_NULL)
		{
			return String();
		}
		else
		{
			return (const TCHAR *)_bstr_t(attribute);
		}
	}
	catch(_com_error&)
	{
		throw DOMException();
	}
}

// MsXMLDOMNodeList
MsXMLDOMNodeList::MsXMLDOMNodeList(MSXML::IXMLDOMNodeListPtr nodeList)
: nodeList(nodeList)
{
}

int MsXMLDOMNodeList::getLength()
{
	long length;
	EXEC(nodeList->get_length(&length));

	return (int)length;
}

XMLDOMNodePtr MsXMLDOMNodeList::item(int index)
{
	try
	{
		MSXML::IXMLDOMNodePtr node = nodeList->item[index];

		if (node->nodeType == MSXML::NODE_ELEMENT)
		{
			return new MsXMLDOMElement(MSXML::IXMLDOMElementPtr(node));
		}
		else
		{
			return new MsXMLDOMNode(node);
		}
	}
	catch(_com_error&)
	{
		throw DOMException();
	}
}

#endif<commit_msg>Added automatic COM Initialization Improved error management<commit_after>/***************************************************************************
                          msxml.cpp  -  XML Helper
                             -------------------
    begin                : sam mai 17 2003
    copyright            : (C) 2003 by Michael CATANZARITI
    email                : mcatan@free.fr
 ***************************************************************************/

/***************************************************************************
* Copyright (C) The Apache Software Foundation. All rights reserved.      *
*                                                                         *
* This software is published under the terms of the Apache Software       *
* License version 1.1, a copy of which has been included with this        *
* distribution in the LICENSE.txt file.                                   *
***************************************************************************/

#define _WIN32_DCOM
#include <log4cxx/config.h>

#ifdef HAVE_MS_XML

#include <log4cxx/helpers/msxml.h>
#include <log4cxx/helpers/loglog.h>
#include <objbase.h>

using namespace log4cxx;
using namespace log4cxx::helpers;

IMPLEMENT_LOG4CXX_OBJECT(MsXMLDOMDocument)
IMPLEMENT_LOG4CXX_OBJECT(MsXMLDOMNodeList)
IMPLEMENT_LOG4CXX_OBJECT(MsXMLDOMNode)
IMPLEMENT_LOG4CXX_OBJECT(MsXMLDOMElement)

#define EXEC(stmt) { HRESULT hr = stmt; if (FAILED(hr)) throw DOMException(); }

// MsXMLDOMNode

MsXMLDOMNode::MsXMLDOMNode(MSXML::IXMLDOMNodePtr node)
: node(node)
{
}

XMLDOMNodeListPtr MsXMLDOMNode::getChildNodes()
{
	MSXML::IXMLDOMNodeListPtr nodeList;
	EXEC(node->get_childNodes(&nodeList));
	return new MsXMLDOMNodeList(nodeList);
}

XMLDOMDocumentPtr MsXMLDOMNode::getOwnerDocument()
{
	MSXML::IXMLDOMDocumentPtr document;
	EXEC(node->get_ownerDocument(&document));
	return new MsXMLDOMDocument(document);
}

// MsXMLDOMDocument

MsXMLDOMDocument::MsXMLDOMDocument(MSXML::IXMLDOMDocumentPtr document)
: document(document)
{
	::CoInitializeEx(0, COINIT_MULTITHREADED);
}

MsXMLDOMDocument::MsXMLDOMDocument()
{
	::CoInitializeEx(0, COINIT_MULTITHREADED);

	HRESULT hRes;
	hRes = document.CreateInstance(L"Msxml2.DOMDocument.3.0");
	if (FAILED(hRes))
	{
		hRes = document.CreateInstance(L"Msxml2.DOMDocument.2.6");
		if (FAILED(hRes))
		{
			hRes = document.CreateInstance(L"Msxml2.DOMDocument");
			if (FAILED(hRes))
			{
				hRes = document.CreateInstance(L"Msxml.DOMDocument");
				if (FAILED(hRes))
				{
					throw DOMException();
				}
			}
		}
	}
}

MsXMLDOMDocument::~MsXMLDOMDocument()
{
	document.Release();
	::CoUninitialize();
}

XMLDOMNodeListPtr MsXMLDOMDocument::getChildNodes()
{
	MSXML::IXMLDOMNodeListPtr nodeList;
	EXEC(document->get_childNodes(&nodeList));
	return new MsXMLDOMNodeList(nodeList);
}

XMLDOMDocumentPtr MsXMLDOMDocument::getOwnerDocument()
{
	return this;
}

void MsXMLDOMDocument::load(const String& fileName)
{
	try
	{
		VARIANT_BOOL bSuccess = document->load(fileName.c_str());

		if (!bSuccess)
		{
			MSXML::IXMLDOMParseErrorPtr parseError = document->parseError;

			// fetch errorcode
			long errorCode = parseError->errorCode;

			_bstr_t reason = parseError->reason;
			long line = parseError->line;
			long linepos = parseError->linepos;

			// remove \n or \r
			int len = reason.length();
			while(len > 0 && (((BSTR)reason)[len -1] == L'\n' ||
				((BSTR)reason)[len -1] == L'\r'))
			{
				((BSTR)reason)[len -1] = L'\0';
				len--;
			}

			USES_CONVERSION;
			LOGLOG_ERROR(_T("Could not open [") << fileName << _T("] : ")
				<< W2T((BSTR)reason) << _T("(line ") << line << _T(", column ")
				<< linepos << _T(")"));
		}

	}
	catch(_com_error&)
	{
		LogLog::error(_T("Could not open [")+fileName+_T("]."));
		throw DOMException();
	}
}

XMLDOMElementPtr MsXMLDOMDocument::getDocumentElement()
{
	MSXML::IXMLDOMElementPtr element;
	EXEC(document->get_documentElement(&element));
	return new MsXMLDOMElement(element);
}

XMLDOMElementPtr MsXMLDOMDocument::getElementById(const String& tagName, const String& elementId)
{
	MSXML::IXMLDOMElementPtr element;

	try
	{
		MSXML::IXMLDOMNodeListPtr list = document->getElementsByTagName(tagName.c_str());
		for (int t=0; t < list->length; t++)
		{
			MSXML::IXMLDOMNodePtr node = list->item[t];
			MSXML::IXMLDOMNamedNodeMapPtr map= node->attributes;
			MSXML::IXMLDOMNodePtr attrNode = map->getNamedItem(L"name");
			_bstr_t nodeValue = attrNode->nodeValue;

			USES_CONVERSION;
			if (elementId == W2T((BSTR)nodeValue))
			{
				element = node;
				break;
			}
		}
	}
	catch(_com_error&)
	{
		throw DOMException();
	}

	return new MsXMLDOMElement(element);
}

// MsXMLDOMElement
MsXMLDOMElement::MsXMLDOMElement(MSXML::IXMLDOMElementPtr element)
: element(element)
{
}

XMLDOMNodeListPtr MsXMLDOMElement::getChildNodes()
{
	MSXML::IXMLDOMNodeListPtr nodeList;
	EXEC(element->get_childNodes(&nodeList));
	return new MsXMLDOMNodeList(nodeList);
}

XMLDOMDocumentPtr MsXMLDOMElement::getOwnerDocument()
{
	MSXML::IXMLDOMDocumentPtr document;
	EXEC(element->get_ownerDocument(&document));
	return new MsXMLDOMDocument(document);
}

String MsXMLDOMElement::getTagName()
{
	try
	{
		_bstr_t tagName = element->tagName;
		USES_CONVERSION;
		return W2T((BSTR)tagName);
	}
	catch(_com_error&)
	{
		throw DOMException();
	}
}

String MsXMLDOMElement::getAttribute(const String& name)
{
	try
	{
		_variant_t attribute = element->getAttribute(name.c_str());
		if (attribute.vt == VT_NULL)
		{
			return String();
		}
		else
		{
			return (const TCHAR *)_bstr_t(attribute);
		}
	}
	catch(_com_error&)
	{
		throw DOMException();
	}
}

// MsXMLDOMNodeList
MsXMLDOMNodeList::MsXMLDOMNodeList(MSXML::IXMLDOMNodeListPtr nodeList)
: nodeList(nodeList)
{
}

int MsXMLDOMNodeList::getLength()
{
	long length;
	EXEC(nodeList->get_length(&length));

	return (int)length;
}

XMLDOMNodePtr MsXMLDOMNodeList::item(int index)
{
	try
	{
		MSXML::IXMLDOMNodePtr node = nodeList->item[index];

		if (node->nodeType == MSXML::NODE_ELEMENT)
		{
			return new MsXMLDOMElement(MSXML::IXMLDOMElementPtr(node));
		}
		else
		{
			return new MsXMLDOMNode(node);
		}
	}
	catch(_com_error&)
	{
		throw DOMException();
	}
}

#endif<|endoftext|>


<commit_before>/*
 * Copyright (C) 2015 Daniel Nicoletti <dantti12@gmail.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; see the file COPYING.LIB. If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */

#include "utils.h"

#include <QTextStream>

using namespace Cutelyst;

QByteArray buildTableDivision(const QList<int> &columnsSize)
{
    QByteArray buffer;
    QTextStream out(&buffer, QIODevice::WriteOnly);
    for (int i = 0; i < columnsSize.size(); ++i) {
        if (i) {
            out << "+";
        } else {
            out << ".";
        }
        out << QByteArray().fill('-', columnsSize[i] + 2).data();
    }
    out << "." << endl;

    return buffer;
}

QByteArray Utils::buildTable(const QList<QStringList> &table, const QStringList &headers, const QString &title)
{
    QList<int> columnsSize;

    if (!headers.isEmpty()) {
        Q_FOREACH (const QString &header, headers) {
            columnsSize.append(header.size());
        }
    } else {
        Q_FOREACH (const QStringList &rows, table) {
            if (columnsSize.isEmpty()) {
                Q_FOREACH (const QString &row, rows) {
                    columnsSize.append(row.size());
                }
            } else if (rows.size() != columnsSize.size()) {
                qFatal("Incomplete table");
            }
        }
    }

    Q_FOREACH (const QStringList &row, table) {
        if (row.size() > columnsSize.size()) {
            qFatal("Incomplete table");
            break;
        }

        for (int i = 0; i < row.size(); ++i) {
            columnsSize[i] = qMax(columnsSize[i], row[i].size());
        }
    }

    // printing
    QByteArray buffer;
    QTextStream out(&buffer, QIODevice::WriteOnly);
    QByteArray div = buildTableDivision(columnsSize);

    if (!title.isEmpty()) {
        out << title << endl;
    }

    // Top line
    out << div;

    if (!headers.isEmpty()) {
        // header titles
        for (int i = 0; i < headers.size(); ++i) {
            out << "| " << headers[i].leftJustified(columnsSize[i]) << ' ';
        }
        out << '|' << endl;

        // header bottom line
        out << div;
    }

    Q_FOREACH (const QStringList &row, table) {
        // content table
        for (int i = 0; i < row.size(); ++i) {
            out << "| " << row[i].leftJustified(columnsSize[i]) << ' ';
        }
        out << '|' << endl;
    }

    // table bottom line
    out << div;

    return buffer;
}
<commit_msg>Use QTextStream setFieldWidth instad of leftJustify<commit_after>/*
 * Copyright (C) 2015 Daniel Nicoletti <dantti12@gmail.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; see the file COPYING.LIB. If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */

#include "utils.h"

#include <QTextStream>

using namespace Cutelyst;

QByteArray buildTableDivision(const QList<int> &columnsSize)
{
    QByteArray buffer;
    QTextStream out(&buffer, QIODevice::WriteOnly);
    for (int i = 0; i < columnsSize.size(); ++i) {
        if (i) {
            out << "+";
        } else {
            out << ".";
        }
        out << QByteArray().fill('-', columnsSize[i] + 2).data();
    }
    out << "." << endl;

    return buffer;
}

QByteArray Utils::buildTable(const QList<QStringList> &table, const QStringList &headers, const QString &title)
{
    QList<int> columnsSize;

    if (!headers.isEmpty()) {
        Q_FOREACH (const QString &header, headers) {
            columnsSize.append(header.size());
        }
    } else {
        Q_FOREACH (const QStringList &rows, table) {
            if (columnsSize.isEmpty()) {
                Q_FOREACH (const QString &row, rows) {
                    columnsSize.append(row.size());
                }
            } else if (rows.size() != columnsSize.size()) {
                qFatal("Incomplete table");
            }
        }
    }

    Q_FOREACH (const QStringList &row, table) {
        if (row.size() > columnsSize.size()) {
            qFatal("Incomplete table");
            break;
        }

        for (int i = 0; i < row.size(); ++i) {
            columnsSize[i] = qMax(columnsSize[i], row[i].size());
        }
    }

    // printing
    QByteArray buffer;
    QTextStream out(&buffer, QIODevice::WriteOnly);
    out.setFieldAlignment(QTextStream::AlignLeft);
    QByteArray div = buildTableDivision(columnsSize);

    if (!title.isEmpty()) {
        out << title << endl;
    }

    // Top line
    out << div;

    if (!headers.isEmpty()) {
        // header titles
        for (int i = 0; i < headers.size(); ++i) {
            out << "| ";

            out.setFieldWidth(columnsSize[i]);
            out << headers[i];

            out.setFieldWidth(0);
            out << ' ';
        }
        out << '|' << endl;

        // header bottom line
        out << div;
    }

    Q_FOREACH (const QStringList &row, table) {
        // content table
        for (int i = 0; i < row.size(); ++i) {
            out << "| ";

            out.setFieldWidth(columnsSize[i]);
            out << row[i];

            out.setFieldWidth(0);
            out << ' ';
        }
        out << '|' << endl;
    }

    // table bottom line
    out << div;

    return buffer;
}
<|endoftext|>


<commit_before>
#pragma once

#include <string>

#include "NamedObject.hpp"

namespace werk
{

/**
 * An abstract class that represents an action to be executed one or more
 * times, but not in an ongoing fashion (should not execute indefinitely).
 *
 * Named to enable easier debugging.
 */
class Action : public NamedObject
{
public:
	Action(const std::string &name) : NamedObject(name) { }

	virtual void execute() = 0;
};

/**
 * Do nothing action.
 */
class NullAction : public Action
{
public:
	NullAction(const std::string &name) : Action(name) { }

	void execute() override { }
};

/**
 * An action that increments a counter every time it is executed. Very useful
 * for testing, and appropriate where an entirely separate `Counter` would be
 * inconvenient.
 */
template <typename T=uint64_t>
class CounterAction : public Action
{
public:
	CounterAction(const std::string &name) : Action(name) { }

	T count() const { return _count; }
	void reset() const { _count = 0; }

	void execute() override { _count += 1; }

private:
	T _count = 0;
};

/**
 * An action that resets a component (many components have a `void reset()` method).
 */
template <typename T>
class ResetAction : public Action
{
public:
	ResetAction(const std::string &name, T &object) :
		Action(name), _object(object) { }

	void execute() override { _object.reset(); }

private:
	T &_object;
};

}
<commit_msg>Implement PairAction and CompoundAction (closing #68)<commit_after>
#pragma once

#include <string>
#include <vector>

#include "NamedObject.hpp"

namespace werk
{

/**
 * An abstract class that represents an action to be executed one or more
 * times, but not in an ongoing fashion (should not execute indefinitely).
 *
 * Named to enable easier debugging.
 */
class Action : public NamedObject
{
public:
	Action(const std::string &name) : NamedObject(name) { }

	virtual void execute() = 0;
};

/**
 * Do nothing action.
 */
class NullAction : public Action
{
public:
	NullAction(const std::string &name) : Action(name) { }

	void execute() override { }
};

/**
 * An action that increments a counter every time it is executed. Very useful
 * for testing, and appropriate where an entirely separate `Counter` would be
 * inconvenient.
 */
template <typename T=uint64_t>
class CounterAction : public Action
{
public:
	CounterAction(const std::string &name) : Action(name) { }

	T count() const { return _count; }
	void reset() const { _count = 0; }

	void execute() override { _count += 1; }

private:
	T _count = 0;
};

/**
 * An action that executes a pair of other actions.
 */
class CompoundAction : public Action
{
public:
	CompoundAction(const std::string &name) : Action(name) { }

	std::vector<Action *> &actions() { return _actions; }
	const std::vector<Action *> &actions() const { return _actions; }

	void execute() override {
		for (Action *action : _actions) {
			action->execute();
		}
	}

private:
	std::vector<Action *> _actions;
};

/**
 * An action that executes a pair of other actions.
 */
class PairAction : public Action
{
public:
	PairAction(const std::string &name, Action *action1, Action *action2) :
		Action(name), _action1(action1), _action2(action2) { }

	void execute() override {
		_action1->execute();
		_action2->execute();
	}

private:
	Action *_action1;
	Action *_action2;
};

/**
 * An action that resets a component (many components have a `void reset()` method).
 */
template <typename T>
class ResetAction : public Action
{
public:
	ResetAction(const std::string &name, T &object) :
		Action(name), _object(object) { }

	void execute() override { _object.reset(); }

private:
	T &_object;
};

}
<|endoftext|>


<commit_before>//===------------------------- mutex.cpp ----------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "mutex"
#include "limits"
#include "system_error"
#include "include/atomic_support.h"
#include "__undef_macros"

_LIBCPP_BEGIN_NAMESPACE_STD
#ifndef _LIBCPP_HAS_NO_THREADS

const defer_lock_t  defer_lock = {};
const try_to_lock_t try_to_lock = {};
const adopt_lock_t  adopt_lock = {};

mutex::~mutex()
{
    __libcpp_mutex_destroy(&__m_);
}

void
mutex::lock()
{
    int ec = __libcpp_mutex_lock(&__m_);
    if (ec)
        __throw_system_error(ec, "mutex lock failed");
}

bool
mutex::try_lock() _NOEXCEPT
{
    return __libcpp_mutex_trylock(&__m_);
}

void
mutex::unlock() _NOEXCEPT
{
    int ec = __libcpp_mutex_unlock(&__m_);
    (void)ec;
    _LIBCPP_ASSERT(ec == 0, "call to mutex::unlock failed");
}

// recursive_mutex

recursive_mutex::recursive_mutex()
{
    int ec = __libcpp_recursive_mutex_init(&__m_);
    if (ec)
        __throw_system_error(ec, "recursive_mutex constructor failed");
}

recursive_mutex::~recursive_mutex()
{
    int e = __libcpp_recursive_mutex_destroy(&__m_);
    (void)e;
    _LIBCPP_ASSERT(e == 0, "call to ~recursive_mutex() failed");
}

void
recursive_mutex::lock()
{
    int ec = __libcpp_recursive_mutex_lock(&__m_);
    if (ec)
        __throw_system_error(ec, "recursive_mutex lock failed");
}

void
recursive_mutex::unlock() _NOEXCEPT
{
    int e = __libcpp_recursive_mutex_unlock(&__m_);
    (void)e;
    _LIBCPP_ASSERT(e == 0, "call to recursive_mutex::unlock() failed");
}

bool
recursive_mutex::try_lock() _NOEXCEPT
{
    return __libcpp_recursive_mutex_trylock(&__m_);
}

// timed_mutex

timed_mutex::timed_mutex()
    : __locked_(false)
{
}

timed_mutex::~timed_mutex()
{
    lock_guard<mutex> _(__m_);
}

void
timed_mutex::lock()
{
    unique_lock<mutex> lk(__m_);
    while (__locked_)
        __cv_.wait(lk);
    __locked_ = true;
}

bool
timed_mutex::try_lock() _NOEXCEPT
{
    unique_lock<mutex> lk(__m_, try_to_lock);
    if (lk.owns_lock() && !__locked_)
    {
        __locked_ = true;
        return true;
    }
    return false;
}

void
timed_mutex::unlock() _NOEXCEPT
{
    lock_guard<mutex> _(__m_);
    __locked_ = false;
    __cv_.notify_one();
}

// recursive_timed_mutex

recursive_timed_mutex::recursive_timed_mutex()
    : __count_(0),
      __id_(0)
{
}

recursive_timed_mutex::~recursive_timed_mutex()
{
    lock_guard<mutex> _(__m_);
}

void
recursive_timed_mutex::lock()
{
    __libcpp_thread_id id = __libcpp_thread_get_current_id();
    unique_lock<mutex> lk(__m_);
    if (__libcpp_thread_id_equal(id, __id_))
    {
        if (__count_ == numeric_limits<size_t>::max())
            __throw_system_error(EAGAIN, "recursive_timed_mutex lock limit reached");
        ++__count_;
        return;
    }
    while (__count_ != 0)
        __cv_.wait(lk);
    __count_ = 1;
    __id_ = id;
}

bool
recursive_timed_mutex::try_lock() _NOEXCEPT
{
    __libcpp_thread_id id = __libcpp_thread_get_current_id();
    unique_lock<mutex> lk(__m_, try_to_lock);
    if (lk.owns_lock() && (__count_ == 0 || __libcpp_thread_id_equal(id, __id_)))
    {
        if (__count_ == numeric_limits<size_t>::max())
            return false;
        ++__count_;
        __id_ = id;
        return true;
    }
    return false;
}

void
recursive_timed_mutex::unlock() _NOEXCEPT
{
    unique_lock<mutex> lk(__m_);
    if (--__count_ == 0)
    {
        __id_ = 0;
        lk.unlock();
        __cv_.notify_one();
    }
}

#endif // !_LIBCPP_HAS_NO_THREADS

// If dispatch_once_f ever handles C++ exceptions, and if one can get to it
// without illegal macros (unexpected macros not beginning with _UpperCase or
// __lowercase), and if it stops spinning waiting threads, then call_once should
// call into dispatch_once_f instead of here. Relevant radar this code needs to
// keep in sync with:  7741191.

#ifndef _LIBCPP_HAS_NO_THREADS
_LIBCPP_SAFE_STATIC static __libcpp_mutex_t mut = _LIBCPP_MUTEX_INITIALIZER;
_LIBCPP_SAFE_STATIC static __libcpp_condvar_t cv = _LIBCPP_CONDVAR_INITIALIZER;
#endif

void __call_once(volatile once_flag::_State_type& flag, void* arg,
                 void (*func)(void*))
{
#if defined(_LIBCPP_HAS_NO_THREADS)
    if (flag == 0)
    {
#ifndef _LIBCPP_NO_EXCEPTIONS
        try
        {
#endif  // _LIBCPP_NO_EXCEPTIONS
            flag = 1;
            func(arg);
            flag = ~once_flag::_State_type(0);
#ifndef _LIBCPP_NO_EXCEPTIONS
        }
        catch (...)
        {
            flag = 0;
            throw;
        }
#endif  // _LIBCPP_NO_EXCEPTIONS
    }
#else // !_LIBCPP_HAS_NO_THREADS
    __libcpp_mutex_lock(&mut);
    while (flag == 1)
        __libcpp_condvar_wait(&cv, &mut);
    if (flag == 0)
    {
#ifndef _LIBCPP_NO_EXCEPTIONS
        try
        {
#endif  // _LIBCPP_NO_EXCEPTIONS
            __libcpp_relaxed_store(&flag, once_flag::_State_type(1));
            __libcpp_mutex_unlock(&mut);
            func(arg);
            __libcpp_mutex_lock(&mut);
            __libcpp_atomic_store(&flag, ~once_flag::_State_type(0),
                                  _AO_Release);
            __libcpp_mutex_unlock(&mut);
            __libcpp_condvar_broadcast(&cv);
#ifndef _LIBCPP_NO_EXCEPTIONS
        }
        catch (...)
        {
            __libcpp_mutex_lock(&mut);
            __libcpp_relaxed_store(&flag, once_flag::_State_type(0));
            __libcpp_mutex_unlock(&mut);
            __libcpp_condvar_broadcast(&cv);
            throw;
        }
#endif  // _LIBCPP_NO_EXCEPTIONS
    }
    else
        __libcpp_mutex_unlock(&mut);
#endif // !_LIBCPP_HAS_NO_THREADS
}

_LIBCPP_END_NAMESPACE_STD
<commit_msg>Fix r359229 which tried to fix r359159...<commit_after>//===------------------------- mutex.cpp ----------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "mutex"
#include "limits"
#include "system_error"
#include "include/atomic_support.h"
#include "__undef_macros"

_LIBCPP_BEGIN_NAMESPACE_STD
#ifndef _LIBCPP_HAS_NO_THREADS

const defer_lock_t  defer_lock = {};
const try_to_lock_t try_to_lock = {};
const adopt_lock_t  adopt_lock = {};

mutex::~mutex() _NOEXCEPT
{
    __libcpp_mutex_destroy(&__m_);
}

void
mutex::lock()
{
    int ec = __libcpp_mutex_lock(&__m_);
    if (ec)
        __throw_system_error(ec, "mutex lock failed");
}

bool
mutex::try_lock() _NOEXCEPT
{
    return __libcpp_mutex_trylock(&__m_);
}

void
mutex::unlock() _NOEXCEPT
{
    int ec = __libcpp_mutex_unlock(&__m_);
    (void)ec;
    _LIBCPP_ASSERT(ec == 0, "call to mutex::unlock failed");
}

// recursive_mutex

recursive_mutex::recursive_mutex()
{
    int ec = __libcpp_recursive_mutex_init(&__m_);
    if (ec)
        __throw_system_error(ec, "recursive_mutex constructor failed");
}

recursive_mutex::~recursive_mutex()
{
    int e = __libcpp_recursive_mutex_destroy(&__m_);
    (void)e;
    _LIBCPP_ASSERT(e == 0, "call to ~recursive_mutex() failed");
}

void
recursive_mutex::lock()
{
    int ec = __libcpp_recursive_mutex_lock(&__m_);
    if (ec)
        __throw_system_error(ec, "recursive_mutex lock failed");
}

void
recursive_mutex::unlock() _NOEXCEPT
{
    int e = __libcpp_recursive_mutex_unlock(&__m_);
    (void)e;
    _LIBCPP_ASSERT(e == 0, "call to recursive_mutex::unlock() failed");
}

bool
recursive_mutex::try_lock() _NOEXCEPT
{
    return __libcpp_recursive_mutex_trylock(&__m_);
}

// timed_mutex

timed_mutex::timed_mutex()
    : __locked_(false)
{
}

timed_mutex::~timed_mutex()
{
    lock_guard<mutex> _(__m_);
}

void
timed_mutex::lock()
{
    unique_lock<mutex> lk(__m_);
    while (__locked_)
        __cv_.wait(lk);
    __locked_ = true;
}

bool
timed_mutex::try_lock() _NOEXCEPT
{
    unique_lock<mutex> lk(__m_, try_to_lock);
    if (lk.owns_lock() && !__locked_)
    {
        __locked_ = true;
        return true;
    }
    return false;
}

void
timed_mutex::unlock() _NOEXCEPT
{
    lock_guard<mutex> _(__m_);
    __locked_ = false;
    __cv_.notify_one();
}

// recursive_timed_mutex

recursive_timed_mutex::recursive_timed_mutex()
    : __count_(0),
      __id_(0)
{
}

recursive_timed_mutex::~recursive_timed_mutex()
{
    lock_guard<mutex> _(__m_);
}

void
recursive_timed_mutex::lock()
{
    __libcpp_thread_id id = __libcpp_thread_get_current_id();
    unique_lock<mutex> lk(__m_);
    if (__libcpp_thread_id_equal(id, __id_))
    {
        if (__count_ == numeric_limits<size_t>::max())
            __throw_system_error(EAGAIN, "recursive_timed_mutex lock limit reached");
        ++__count_;
        return;
    }
    while (__count_ != 0)
        __cv_.wait(lk);
    __count_ = 1;
    __id_ = id;
}

bool
recursive_timed_mutex::try_lock() _NOEXCEPT
{
    __libcpp_thread_id id = __libcpp_thread_get_current_id();
    unique_lock<mutex> lk(__m_, try_to_lock);
    if (lk.owns_lock() && (__count_ == 0 || __libcpp_thread_id_equal(id, __id_)))
    {
        if (__count_ == numeric_limits<size_t>::max())
            return false;
        ++__count_;
        __id_ = id;
        return true;
    }
    return false;
}

void
recursive_timed_mutex::unlock() _NOEXCEPT
{
    unique_lock<mutex> lk(__m_);
    if (--__count_ == 0)
    {
        __id_ = 0;
        lk.unlock();
        __cv_.notify_one();
    }
}

#endif // !_LIBCPP_HAS_NO_THREADS

// If dispatch_once_f ever handles C++ exceptions, and if one can get to it
// without illegal macros (unexpected macros not beginning with _UpperCase or
// __lowercase), and if it stops spinning waiting threads, then call_once should
// call into dispatch_once_f instead of here. Relevant radar this code needs to
// keep in sync with:  7741191.

#ifndef _LIBCPP_HAS_NO_THREADS
_LIBCPP_SAFE_STATIC static __libcpp_mutex_t mut = _LIBCPP_MUTEX_INITIALIZER;
_LIBCPP_SAFE_STATIC static __libcpp_condvar_t cv = _LIBCPP_CONDVAR_INITIALIZER;
#endif

void __call_once(volatile once_flag::_State_type& flag, void* arg,
                 void (*func)(void*))
{
#if defined(_LIBCPP_HAS_NO_THREADS)
    if (flag == 0)
    {
#ifndef _LIBCPP_NO_EXCEPTIONS
        try
        {
#endif  // _LIBCPP_NO_EXCEPTIONS
            flag = 1;
            func(arg);
            flag = ~once_flag::_State_type(0);
#ifndef _LIBCPP_NO_EXCEPTIONS
        }
        catch (...)
        {
            flag = 0;
            throw;
        }
#endif  // _LIBCPP_NO_EXCEPTIONS
    }
#else // !_LIBCPP_HAS_NO_THREADS
    __libcpp_mutex_lock(&mut);
    while (flag == 1)
        __libcpp_condvar_wait(&cv, &mut);
    if (flag == 0)
    {
#ifndef _LIBCPP_NO_EXCEPTIONS
        try
        {
#endif  // _LIBCPP_NO_EXCEPTIONS
            __libcpp_relaxed_store(&flag, once_flag::_State_type(1));
            __libcpp_mutex_unlock(&mut);
            func(arg);
            __libcpp_mutex_lock(&mut);
            __libcpp_atomic_store(&flag, ~once_flag::_State_type(0),
                                  _AO_Release);
            __libcpp_mutex_unlock(&mut);
            __libcpp_condvar_broadcast(&cv);
#ifndef _LIBCPP_NO_EXCEPTIONS
        }
        catch (...)
        {
            __libcpp_mutex_lock(&mut);
            __libcpp_relaxed_store(&flag, once_flag::_State_type(0));
            __libcpp_mutex_unlock(&mut);
            __libcpp_condvar_broadcast(&cv);
            throw;
        }
#endif  // _LIBCPP_NO_EXCEPTIONS
    }
    else
        __libcpp_mutex_unlock(&mut);
#endif // !_LIBCPP_HAS_NO_THREADS
}

_LIBCPP_END_NAMESPACE_STD
<|endoftext|>