<commit_before>/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/import/chips/p9/procedures/hwp/memory/lib/dimm/ddr4/mrs02.C $ */
/*                                                                        */
/* OpenPOWER HostBoot Project                                             */
/*                                                                        */
/* Contributors Listed Below - COPYRIGHT 2016                             */
/* [+] International Business Machines Corp.                              */
/*                                                                        */
/*                                                                        */
/* Licensed under the Apache License, Version 2.0 (the "License");        */
/* you may not use this file except in compliance with the License.       */
/* You may obtain a copy of the License at                                */
/*                                                                        */
/*     http://www.apache.org/licenses/LICENSE-2.0                         */
/*                                                                        */
/* Unless required by applicable law or agreed to in writing, software    */
/* distributed under the License is distributed on an "AS IS" BASIS,      */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or        */
/* implied. See the License for the specific language governing           */
/* permissions and limitations under the License.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */

///
/// @file mrs02.C
/// @brief Run and manage the DDR4 MRS02 loading
///
// *HWP HWP Owner: Brian Silver <bsilver@us.ibm.com>
// *HWP HWP Backup: Andre Marin <aamarin@us.ibm.com>
// *HWP Team: Memory
// *HWP Level: 1
// *HWP Consumed by: FSP:HB

#include <fapi2.H>

#include <mss.H>
#include <lib/dimm/ddr4/mrs_load_ddr4.H>

using fapi2::TARGET_TYPE_MCBIST;
using fapi2::TARGET_TYPE_DIMM;

using fapi2::FAPI2_RC_SUCCESS;

namespace mss
{

namespace ddr4
{

///
/// @brief mrs02_data ctor
/// @param[in] a fapi2::TARGET_TYPE_DIMM target
/// @param[out] fapi2::ReturnCode FAPI2_RC_SUCCESS iff ok
///
mrs02_data::mrs02_data( const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target, fapi2::ReturnCode& o_rc ):
    iv_lpasr(0),
    iv_cwl(0),
    iv_write_crc(0)
{
    FAPI_TRY( mss::eff_dram_lpasr(i_target, iv_lpasr) );
    FAPI_TRY( mss::eff_dram_cwl(i_target, iv_cwl) );
    FAPI_TRY( mss::vpd_mt_dram_rtt_wr(i_target, &(iv_dram_rtt_wr[0])) );
    FAPI_TRY( mss::eff_write_crc(i_target, iv_write_crc) );

    o_rc = fapi2::FAPI2_RC_SUCCESS;
    return;

fapi_try_exit:
    o_rc = fapi2::current_err;
    FAPI_ERR("unable to get attributes for mrs0");
    return;
}

///
/// @brief Configure the ARR0 of the CCS instruction for mrs02
/// @param[in] i_target a fapi2::Target<TARGET_TYPE_DIMM>
/// @param[in,out] io_inst the instruction to fixup
/// @param[in] i_rank the rank in question
/// @return FAPI2_RC_SUCCESS iff OK
///
fapi2::ReturnCode mrs02(const fapi2::Target<TARGET_TYPE_DIMM>& i_target,
                        ccs::instruction_t<TARGET_TYPE_MCBIST>& io_inst,
                        const uint64_t i_rank)
{
    // Check to make sure our ctor worked ok
    mrs02_data l_data( i_target, fapi2::current_err );
    FAPI_TRY( fapi2::current_err, "Unable to construct MRS02 data from attributes");
    FAPI_TRY( mrs02(i_target, l_data, io_inst, i_rank) );

fapi_try_exit:
    return fapi2::current_err;
}

///
/// @brief Configure the ARR0 of the CCS instruction for mrs02, data object as input
/// @param[in] i_target a fapi2::Target<fapi2::TARGET_TYPE_DIMM>
/// @param[in] i_data an mrs02_data object, filled in
/// @param[in,out] io_inst the instruction to fixup
/// @param[in] i_rank the rank in question
/// @return FAPI2_RC_SUCCESS iff OK
///
fapi2::ReturnCode mrs02(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target,
                        const mrs02_data& i_data,
                        ccs::instruction_t<fapi2::TARGET_TYPE_MCBIST>& io_inst,
                        const uint64_t i_rank)
{
    // Index this by subtracting 9 from the CWL attribute value. The table maps CWL attribute value
    // (in clks) to the bit setting in MR2. See the table in the JEDEC spec for the mapping.
    constexpr uint64_t LOWEST_CWL = 9;
    constexpr uint64_t CWL_COUNT = 12;
    //                                          9     10     11     12        14         16       18        20
    constexpr uint8_t cwl_map[CWL_COUNT] = { 0b000, 0b001, 0b010, 0b011, 0, 0b100, 0, 0b101, 0, 0b110, 0, 0b111 };

    fapi2::buffer<uint8_t> l_cwl_buffer;
    fapi2::buffer<uint8_t> l_rtt_wr_buffer;

    FAPI_ASSERT((i_data.iv_cwl >= LOWEST_CWL) && (i_data.iv_cwl < (LOWEST_CWL + CWL_COUNT)),
                fapi2::MSS_BAD_MR_PARAMETER()
                .set_MR_NUMBER(0)
                .set_PARAMETER(CAS_WRITE_LATENCY)
                .set_PARAMETER_VALUE(i_data.iv_cwl)
                .set_DIMM_IN_ERROR(i_target),
                "Bad value for CWL: %d (%s)", i_data.iv_cwl, mss::c_str(i_target));

    l_cwl_buffer = cwl_map[i_data.iv_cwl - LOWEST_CWL];

    switch (i_data.iv_dram_rtt_wr[i_rank])
    {
        case fapi2::ENUM_ATTR_MSS_VPD_MT_DRAM_RTT_WR_DISABLE:
            l_rtt_wr_buffer = 0b000;
            break;

        case fapi2::ENUM_ATTR_MSS_VPD_MT_DRAM_RTT_WR_HIGHZ:
            l_rtt_wr_buffer = 0b011;
            break;

        case fapi2::ENUM_ATTR_MSS_VPD_MT_DRAM_RTT_WR_OHM240:
            l_rtt_wr_buffer = 0b010;
            break;

        case fapi2::ENUM_ATTR_MSS_VPD_MT_DRAM_RTT_WR_OHM120:
            l_rtt_wr_buffer = 0b001;
            break;

        default:
            FAPI_ERR("unknown RTT_WR 0x%x (%s rank %d), dynamic odt off",
                     i_data.iv_dram_rtt_wr[i_rank], mss::c_str(i_target), i_rank);
            l_rtt_wr_buffer = 0b000;
            break;
    };

    // Printed here as opposed to the ctor as it uses the rank information
    FAPI_INF("MR2 rank %d attributes: LPASR: 0x%x, CWL: 0x%x(0x%x), RTT_WR: 0x%x(0x%x), WRITE_CRC: 0x%x", i_rank,
             uint8_t(i_data.iv_lpasr), i_data.iv_cwl, uint8_t(l_cwl_buffer),
             i_data.iv_dram_rtt_wr[i_rank], uint8_t(l_rtt_wr_buffer), i_data.iv_write_crc);

    mss::swizzle<A3, 3, 7>(l_cwl_buffer, io_inst.arr0);

    mss::swizzle<A6, 2, 7>(fapi2::buffer<uint8_t>(i_data.iv_lpasr), io_inst.arr0);

    mss::swizzle<A9, 3, 7>(l_rtt_wr_buffer, io_inst.arr0);

    io_inst.arr0.writeBit<A12>(i_data.iv_write_crc);

    FAPI_INF("MR2: 0x%016llx", uint64_t(io_inst.arr0));

    return fapi2::FAPI2_RC_SUCCESS;

fapi_try_exit:
    return fapi2::current_err;
}

///
/// @brief Given a CCS instruction which contains address bits with an encoded MRS2,
/// decode and trace the contents
/// @param[in] i_inst the CCS instruction
/// @param[in] i_rank ths rank in question
/// @return FAPI2_RC_SUCCESS iff ok
///
fapi2::ReturnCode mrs02_decode(const ccs::instruction_t<TARGET_TYPE_MCBIST>& i_inst,
                               const uint64_t i_rank)
{
    fapi2::buffer<uint8_t> l_lpasr;
    fapi2::buffer<uint8_t> l_cwl;
    fapi2::buffer<uint8_t> l_rtt_wr;

    uint8_t l_write_crc = i_inst.arr0.getBit<A12>();
    mss::swizzle<5, 3, A5>(i_inst.arr0, l_cwl);
    mss::swizzle<6, 2, A7>(i_inst.arr0, l_lpasr);
    mss::swizzle<5, 3, A11>(i_inst.arr0, l_rtt_wr);

    FAPI_INF("MR2 rank %d deocode: LPASR: 0x%x, CWL: 0x%x, RTT_WR: 0x%x, WRITE_CRC: 0x%x", i_rank,
             uint8_t(l_lpasr), uint8_t(l_cwl), uint8_t(l_rtt_wr), l_write_crc);

    return FAPI2_RC_SUCCESS;
}

fapi2::ReturnCode (*mrs02_data::make_ccs_instruction)(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target,
        const mrs02_data& i_data,
        ccs::instruction_t<fapi2::TARGET_TYPE_MCBIST>& io_inst,
        const uint64_t i_rank) = &mrs02;

fapi2::ReturnCode (*mrs02_data::decode)(const ccs::instruction_t<fapi2::TARGET_TYPE_MCBIST>& i_inst,
                                        const uint64_t i_rank) = &mrs02_decode;

} // ns ddr4

} // ns mss
<commit_msg>Fix DRAM_RTT_WR<commit_after>/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/import/chips/p9/procedures/hwp/memory/lib/dimm/ddr4/mrs02.C $ */
/*                                                                        */
/* OpenPOWER HostBoot Project                                             */
/*                                                                        */
/* Contributors Listed Below - COPYRIGHT 2016                             */
/* [+] International Business Machines Corp.                              */
/*                                                                        */
/*                                                                        */
/* Licensed under the Apache License, Version 2.0 (the "License");        */
/* you may not use this file except in compliance with the License.       */
/* You may obtain a copy of the License at                                */
/*                                                                        */
/*     http://www.apache.org/licenses/LICENSE-2.0                         */
/*                                                                        */
/* Unless required by applicable law or agreed to in writing, software    */
/* distributed under the License is distributed on an "AS IS" BASIS,      */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or        */
/* implied. See the License for the specific language governing           */
/* permissions and limitations under the License.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */

///
/// @file mrs02.C
/// @brief Run and manage the DDR4 MRS02 loading
///
// *HWP HWP Owner: Brian Silver <bsilver@us.ibm.com>
// *HWP HWP Backup: Andre Marin <aamarin@us.ibm.com>
// *HWP Team: Memory
// *HWP Level: 1
// *HWP Consumed by: FSP:HB

#include <fapi2.H>

#include <mss.H>
#include <lib/dimm/ddr4/mrs_load_ddr4.H>

using fapi2::TARGET_TYPE_MCBIST;
using fapi2::TARGET_TYPE_DIMM;

using fapi2::FAPI2_RC_SUCCESS;

namespace mss
{

namespace ddr4
{

///
/// @brief mrs02_data ctor
/// @param[in] a fapi2::TARGET_TYPE_DIMM target
/// @param[out] fapi2::ReturnCode FAPI2_RC_SUCCESS iff ok
///
mrs02_data::mrs02_data( const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target, fapi2::ReturnCode& o_rc ):
    iv_lpasr(0),
    iv_cwl(0),
    iv_write_crc(0)
{
    FAPI_TRY( mss::eff_dram_lpasr(i_target, iv_lpasr) );
    FAPI_TRY( mss::eff_dram_cwl(i_target, iv_cwl) );
    FAPI_TRY( mss::vpd_mt_dram_rtt_wr(i_target, &(iv_dram_rtt_wr[0])) );
    FAPI_TRY( mss::eff_write_crc(i_target, iv_write_crc) );

    o_rc = fapi2::FAPI2_RC_SUCCESS;
    return;

fapi_try_exit:
    o_rc = fapi2::current_err;
    FAPI_ERR("unable to get attributes for mrs0");
    return;
}

///
/// @brief Configure the ARR0 of the CCS instruction for mrs02
/// @param[in] i_target a fapi2::Target<TARGET_TYPE_DIMM>
/// @param[in,out] io_inst the instruction to fixup
/// @param[in] i_rank the rank in question
/// @return FAPI2_RC_SUCCESS iff OK
///
fapi2::ReturnCode mrs02(const fapi2::Target<TARGET_TYPE_DIMM>& i_target,
                        ccs::instruction_t<TARGET_TYPE_MCBIST>& io_inst,
                        const uint64_t i_rank)
{
    // Check to make sure our ctor worked ok
    mrs02_data l_data( i_target, fapi2::current_err );
    FAPI_TRY( fapi2::current_err, "Unable to construct MRS02 data from attributes");
    FAPI_TRY( mrs02(i_target, l_data, io_inst, i_rank) );

fapi_try_exit:
    return fapi2::current_err;
}

///
/// @brief Configure the ARR0 of the CCS instruction for mrs02, data object as input
/// @param[in] i_target a fapi2::Target<fapi2::TARGET_TYPE_DIMM>
/// @param[in] i_data an mrs02_data object, filled in
/// @param[in,out] io_inst the instruction to fixup
/// @param[in] i_rank the rank in question
/// @return FAPI2_RC_SUCCESS iff OK
///
fapi2::ReturnCode mrs02(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target,
                        const mrs02_data& i_data,
                        ccs::instruction_t<fapi2::TARGET_TYPE_MCBIST>& io_inst,
                        const uint64_t i_rank)
{
    // Index this by subtracting 9 from the CWL attribute value. The table maps CWL attribute value
    // (in clks) to the bit setting in MR2. See the table in the JEDEC spec for the mapping.
    constexpr uint64_t LOWEST_CWL = 9;
    constexpr uint64_t CWL_COUNT = 12;
    //                                          9     10     11     12        14         16       18        20
    constexpr uint8_t cwl_map[CWL_COUNT] = { 0b000, 0b001, 0b010, 0b011, 0, 0b100, 0, 0b101, 0, 0b110, 0, 0b111 };

    fapi2::buffer<uint8_t> l_cwl_buffer;
    fapi2::buffer<uint8_t> l_rtt_wr_buffer;

    FAPI_ASSERT((i_data.iv_cwl >= LOWEST_CWL) && (i_data.iv_cwl < (LOWEST_CWL + CWL_COUNT)),
                fapi2::MSS_BAD_MR_PARAMETER()
                .set_MR_NUMBER(0)
                .set_PARAMETER(CAS_WRITE_LATENCY)
                .set_PARAMETER_VALUE(i_data.iv_cwl)
                .set_DIMM_IN_ERROR(i_target),
                "Bad value for CWL: %d (%s)", i_data.iv_cwl, mss::c_str(i_target));

    l_cwl_buffer = cwl_map[i_data.iv_cwl - LOWEST_CWL];

    switch (i_data.iv_dram_rtt_wr[i_rank])
    {
        case fapi2::ENUM_ATTR_MSS_VPD_MT_DRAM_RTT_WR_DISABLE:
            l_rtt_wr_buffer = 0b000;
            break;

        case fapi2::ENUM_ATTR_MSS_VPD_MT_DRAM_RTT_WR_HIGHZ:
            l_rtt_wr_buffer = 0b011;
            break;

        case fapi2::ENUM_ATTR_MSS_VPD_MT_DRAM_RTT_WR_OHM240:
            l_rtt_wr_buffer = 0b010;
            break;

        case fapi2::ENUM_ATTR_MSS_VPD_MT_DRAM_RTT_WR_OHM80:
            l_rtt_wr_buffer = 0b100;
            break;

        case fapi2::ENUM_ATTR_MSS_VPD_MT_DRAM_RTT_WR_OHM120:
            l_rtt_wr_buffer = 0b001;
            break;

        default:
            FAPI_ERR("unknown RTT_WR 0x%x (%s rank %d), dynamic odt off",
                     i_data.iv_dram_rtt_wr[i_rank], mss::c_str(i_target), i_rank);
            l_rtt_wr_buffer = 0b000;
            break;
    };

    // Printed here as opposed to the ctor as it uses the rank information
    FAPI_INF("MR2 rank %d attributes: LPASR: 0x%x, CWL: 0x%x(0x%x), RTT_WR: 0x%x(0x%x), WRITE_CRC: 0x%x", i_rank,
             uint8_t(i_data.iv_lpasr), i_data.iv_cwl, uint8_t(l_cwl_buffer),
             i_data.iv_dram_rtt_wr[i_rank], uint8_t(l_rtt_wr_buffer), i_data.iv_write_crc);

    mss::swizzle<A3, 3, 7>(l_cwl_buffer, io_inst.arr0);

    mss::swizzle<A6, 2, 7>(fapi2::buffer<uint8_t>(i_data.iv_lpasr), io_inst.arr0);

    mss::swizzle<A9, 3, 7>(l_rtt_wr_buffer, io_inst.arr0);

    io_inst.arr0.writeBit<A12>(i_data.iv_write_crc);

    FAPI_INF("MR2: 0x%016llx", uint64_t(io_inst.arr0));

    return fapi2::FAPI2_RC_SUCCESS;

fapi_try_exit:
    return fapi2::current_err;
}

///
/// @brief Given a CCS instruction which contains address bits with an encoded MRS2,
/// decode and trace the contents
/// @param[in] i_inst the CCS instruction
/// @param[in] i_rank ths rank in question
/// @return FAPI2_RC_SUCCESS iff ok
///
fapi2::ReturnCode mrs02_decode(const ccs::instruction_t<TARGET_TYPE_MCBIST>& i_inst,
                               const uint64_t i_rank)
{
    fapi2::buffer<uint8_t> l_lpasr;
    fapi2::buffer<uint8_t> l_cwl;
    fapi2::buffer<uint8_t> l_rtt_wr;

    uint8_t l_write_crc = i_inst.arr0.getBit<A12>();
    mss::swizzle<5, 3, A5>(i_inst.arr0, l_cwl);
    mss::swizzle<6, 2, A7>(i_inst.arr0, l_lpasr);
    mss::swizzle<5, 3, A11>(i_inst.arr0, l_rtt_wr);

    FAPI_INF("MR2 rank %d deocode: LPASR: 0x%x, CWL: 0x%x, RTT_WR: 0x%x, WRITE_CRC: 0x%x", i_rank,
             uint8_t(l_lpasr), uint8_t(l_cwl), uint8_t(l_rtt_wr), l_write_crc);

    return FAPI2_RC_SUCCESS;
}

fapi2::ReturnCode (*mrs02_data::make_ccs_instruction)(const fapi2::Target<fapi2::TARGET_TYPE_DIMM>& i_target,
        const mrs02_data& i_data,
        ccs::instruction_t<fapi2::TARGET_TYPE_MCBIST>& io_inst,
        const uint64_t i_rank) = &mrs02;

fapi2::ReturnCode (*mrs02_data::decode)(const ccs::instruction_t<fapi2::TARGET_TYPE_MCBIST>& i_inst,
                                        const uint64_t i_rank) = &mrs02_decode;

} // ns ddr4

} // ns mss
<|endoftext|>


<commit_before>/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/import/chips/p9/procedures/hwp/pm/p9_pm_get_poundv_bucket.H $ */
/*                                                                        */
/* OpenPOWER HostBoot Project                                             */
/*                                                                        */
/* Contributors Listed Below - COPYRIGHT 2015,2019                        */
/* [+] International Business Machines Corp.                              */
/*                                                                        */
/*                                                                        */
/* Licensed under the Apache License, Version 2.0 (the "License");        */
/* you may not use this file except in compliance with the License.       */
/* You may obtain a copy of the License at                                */
/*                                                                        */
/*     http://www.apache.org/licenses/LICENSE-2.0                         */
/*                                                                        */
/* Unless required by applicable law or agreed to in writing, software    */
/* distributed under the License is distributed on an "AS IS" BASIS,      */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or        */
/* implied. See the License for the specific language governing           */
/* permissions and limitations under the License.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */
///
/// @file p9_pm_get_poundv_bucket.H
/// @brief Provide structure for voltageBucketData so that info from #V keyword
/// can be stored inside of this struct.
/// Also define prototype for p9_pm_get_poundv_bucket
///

#ifndef _P9_PM_GET_POUND_V_BUCKET_H_
#define _P9_PM_GET_POUND_V_BUCKET_H_

//------------------------------------------------------------------------------
// Includes
//------------------------------------------------------------------------------
#include <fapi2.H>

namespace fapi2
{

//Represents the data contained within a single #V (version=2) bucket
//Note: This structure should not be assumed to be binary identical
//to the contents of the VPD itself.
//Pack the struct so that we can get a consistent size with no
//extra padding
typedef struct __attribute__((__packed__))  voltageBucketData
{
    // bucket Id
    uint8_t bucketId;
    // Nominal
    uint16_t nomFreq;
    uint16_t VddNomVltg;
    uint16_t IddNomCurr;
    uint16_t VcsNomVltg;
    uint16_t IcsNomCurr;
    // PowerSave
    uint16_t PSFreq;
    uint16_t VddPSVltg;
    uint16_t IddPSCurr;
    uint16_t VcsPSVltg;
    uint16_t IcsPSCurr;
    // Turbo
    uint16_t turboFreq;
    uint16_t VddTurboVltg;
    uint16_t IddTurboCurr;
    uint16_t VcsTurboVltg;
    uint16_t IcsTurboCurr;
    //Ultra Turbo
    uint16_t uTurboFreq;
    uint16_t VddUTurboVltg;
    uint16_t IddUTurboCurr;
    uint16_t VcsUTurboVltg;
    uint16_t IcsUTurboCurr;
    //PowerBus
    uint16_t pbFreq;
    uint16_t VdnPbVltg;
    uint16_t IdnPbCurr;
    uint16_t VcsPbVltg;
    uint16_t IcsPbCurr;
} voltageBucketData_t;

}

//------------------------------------------------------------------------------
// Function prototype
//------------------------------------------------------------------------------
///
/// @brief Read #V bucket data from LRP record based upon
///        bucket ID read off of parent proc chip. This function
///        will pass out a structured format of the #V data of correct
///        endianness.
/// @param[in] i_target EQ chiplet target
/// @param[in] o_data structured data describing the #v bucket used
/// @return FAPI2_RC_SUCCESS if success, else error code.
///
fapi2::ReturnCode p9_pm_get_poundv_bucket(
    const fapi2::Target<fapi2::TARGET_TYPE_EQ>& i_target,
    fapi2::voltageBucketData_t& o_data);

///
/// @brief Read #V bucket data from LRP record based upon
///        bucket ID read off of parent proc chip. This function
///        will pass out array of bytes as they are read from the VPD
///        NOTE: This might be incorrect endianness
/// @param[in] i_target EQ chiplet target
/// @param[in] o_data bytes read from #V keyword of an LRP record
/// @return FAPI2_RC_SUCCESS if success, else error code.
///
fapi2::ReturnCode p9_pm_get_poundv_bucket_attr(
    const fapi2::Target<fapi2::TARGET_TYPE_EQ>& i_target,
    uint8_t* o_data);

#endif // _P9_PM_GET_POUND_V_BUCKET_H_
<commit_msg>splitting up functionality to bring p9_pm_get_poundv_attr accessible to cronus<commit_after>/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/import/chips/p9/procedures/hwp/pm/p9_pm_get_poundv_bucket.H $ */
/*                                                                        */
/* OpenPOWER HostBoot Project                                             */
/*                                                                        */
/* Contributors Listed Below - COPYRIGHT 2015,2019                        */
/* [+] International Business Machines Corp.                              */
/*                                                                        */
/*                                                                        */
/* Licensed under the Apache License, Version 2.0 (the "License");        */
/* you may not use this file except in compliance with the License.       */
/* You may obtain a copy of the License at                                */
/*                                                                        */
/*     http://www.apache.org/licenses/LICENSE-2.0                         */
/*                                                                        */
/* Unless required by applicable law or agreed to in writing, software    */
/* distributed under the License is distributed on an "AS IS" BASIS,      */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or        */
/* implied. See the License for the specific language governing           */
/* permissions and limitations under the License.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */
///
/// @file p9_pm_get_poundv_bucket.H
/// @brief Provide structure for voltageBucketData so that info from #V keyword
/// can be stored inside of this struct.
/// Also define prototype for p9_pm_get_poundv_bucket
///

#ifndef _P9_PM_GET_POUND_V_BUCKET_H_
#define _P9_PM_GET_POUND_V_BUCKET_H_

//------------------------------------------------------------------------------
// Includes
//------------------------------------------------------------------------------
#include <fapi2.H>

namespace fapi2
{

//Represents the data contained within a single #V (version=2) bucket
//Note: This structure should not be assumed to be binary identical
//to the contents of the VPD itself.
//Pack the struct so that we can get a consistent size with no
//extra padding
typedef struct __attribute__((__packed__))  voltageBucketData
{
    // bucket Id
    uint8_t bucketId;
    // Nominal
    uint16_t nomFreq;
    uint16_t VddNomVltg;
    uint16_t IddNomCurr;
    uint16_t VcsNomVltg;
    uint16_t IcsNomCurr;
    // PowerSave
    uint16_t PSFreq;
    uint16_t VddPSVltg;
    uint16_t IddPSCurr;
    uint16_t VcsPSVltg;
    uint16_t IcsPSCurr;
    // Turbo
    uint16_t turboFreq;
    uint16_t VddTurboVltg;
    uint16_t IddTurboCurr;
    uint16_t VcsTurboVltg;
    uint16_t IcsTurboCurr;
    //Ultra Turbo
    uint16_t uTurboFreq;
    uint16_t VddUTurboVltg;
    uint16_t IddUTurboCurr;
    uint16_t VcsUTurboVltg;
    uint16_t IcsUTurboCurr;
    //PowerBus
    uint16_t pbFreq;
    uint16_t VdnPbVltg;
    uint16_t IdnPbCurr;
    uint16_t VcsPbVltg;
    uint16_t IcsPbCurr;
} voltageBucketData_t;

}

//------------------------------------------------------------------------------
// Function prototype
//------------------------------------------------------------------------------
///
/// @brief Read #V bucket data from LRP record based upon
///        bucket ID read off of parent proc chip. This function
///        will pass out a structured format of the #V data of correct
///        endianness.
/// @param[in] i_target EQ chiplet target
/// @param[in] o_data structured data describing the #v bucket used
/// @return FAPI2_RC_SUCCESS if success, else error code.
///
fapi2::ReturnCode p9_pm_get_poundv_bucket(
    const fapi2::Target<fapi2::TARGET_TYPE_EQ>& i_target,
    fapi2::voltageBucketData_t& o_data);

#endif // _P9_PM_GET_POUND_V_BUCKET_H_
<|endoftext|>


<commit_before>// Define this to enable debugging
// #define BOOST_SPIRIT_QI_DEBUG

#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/variant/recursive_variant.hpp>
#include <boost/variant/apply_visitor.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/include/phoenix_function.hpp>
#include <boost/optional.hpp>

#include <iostream>
#include <string>

namespace ast
{
    struct Nil;

    struct Signed;

    struct Expression;

    struct RightAssocExpr;

    struct Arg;

    struct FunctionCall;

    typedef boost::variant<
            Nil
          , double
          , boost::recursive_wrapper< Signed >
          , boost::recursive_wrapper< Expression >
          , boost::recursive_wrapper< RightAssocExpr >
          , boost::recursive_wrapper< Arg >
          , boost::recursive_wrapper< FunctionCall >
        >
    Operand;

    struct Nil
    {
    };

    inline std::ostream & operator<<( std::ostream & out, Nil ) { out << "nil"; return out; }

    struct Signed
    {
        char sign_;
        Operand operand_;
    };

    struct Arg
    {
        char digit_;
    };

    struct RightAssocExpr
    {
        Operand base_;
        boost::optional< Operand > exp_;
    };

    struct FunctionCall
    {
        std::string name_;
        Operand arg_;
    };

    struct Operation
    {
        std::string operator_;
        Operand operand_;
    };

    struct Expression
    {
        Operand head_;
        std::list< Operation > tail_;
    };

}

BOOST_FUSION_ADAPT_STRUCT(
    ast::Signed,
    ( char, sign_ )
    ( ast::Operand, operand_ )
)

BOOST_FUSION_ADAPT_STRUCT(
    ast::Arg,
    ( char, digit_ )
)

BOOST_FUSION_ADAPT_STRUCT(
    ast::RightAssocExpr,
    ( ast::Operand, base_ )
    ( boost::optional< ast::Operand >, exp_ )
)

BOOST_FUSION_ADAPT_STRUCT(
    ast::FunctionCall,
    ( std::string, name_ )
    ( ast::Operand, arg_ )
)

BOOST_FUSION_ADAPT_STRUCT(
    ast::Operation,
    ( std::string, operator_ )
    ( ast::Operand, operand_ )
)

BOOST_FUSION_ADAPT_STRUCT(
    ast::Expression,
    ( ast::Operand, head_ )
    ( std::list< ast::Operation >, tail_ )
)

namespace ast
{
    struct Eval
    {
    public:

        typedef double result_type;

    private:

        template< typename... TDoubles >
        void assign( double * args, int index, double && head, TDoubles &&... tail )
        {
            args[ index ] = head;
            assign( args, index+1, tail... );
        }

        void assign( double * args, int index )
        {
        }

    public:

        template< typename... Doubles >
        Eval( Doubles &&... ds )
        {
            static_assert( sizeof...( Doubles ) < 10, "sizeof...(Doubles) has to be less then 10" );
            args_[ 0 ] = sizeof...( Doubles );

            assign( & args_[ 0 ], 1, std::forward< Doubles >( ds )... );
        }

        double operator()( Nil ) const
        {
            BOOST_ASSERT( 0 ); return 0;
        }

        double operator()( double n ) const
        {
            return n;
        }

        double operator()( Operation const & x, double lhs ) const
        {
            if( x.operator_ == "&&" )
            {
                if( lhs ){
                    return boost::apply_visitor( *this, x.operand_ );
                }
                else{
                    return 0;
                }
            }

            if( x.operator_ == "||" )
            {
                if( lhs ){
                    return 1;
                }
                else{
                    return boost::apply_visitor( *this, x.operand_ );
                }
            }

            double const rhs = boost::apply_visitor( *this, x.operand_ );

            if( x.operator_ == "+" ){
                return lhs + rhs;
            }

            if( x.operator_ == "-" ){
                return lhs - rhs;
            }

            if( x.operator_ == "*" ){
                return lhs * rhs;
            }

            if( x.operator_ == "/" ){
                return lhs / rhs;
            }

            if( x.operator_ == "<" ){
                return lhs < rhs;
            }

            if( x.operator_ == "<=" ){
                return lhs <= rhs;
            }

            if( x.operator_ == ">" ){
                return lhs > rhs;
            }

            if( x.operator_ == ">=" ){
                return lhs >= rhs;
            }

            if( x.operator_ == "==" ){
                return lhs == rhs;
            }

            if( x.operator_ == "!=" ){
                return lhs != rhs;
            }

            return 0;
        }

        double operator()( Signed const & x ) const
        {
            double const rhs = boost::apply_visitor( *this, x.operand_ );

            switch( x.sign_ )
            {
                case '-': return -rhs;
                case '+': return +rhs;
                case '!': return ! (bool)rhs;
                default: BOOST_ASSERT( 0 );
            }
        }

        double operator()( Arg const & x ) const
        {
            return args_[ x.digit_ - '0' ];
        }

        double operator()( RightAssocExpr const & x ) const
        {
            double const base = boost::apply_visitor( *this, x.base_ );

            if( ! x.exp_ ){
                return base;
            }

            double const exp = boost::apply_visitor( *this, x.exp_.get() );

            return std::pow( base, exp );
        }

        double operator()( FunctionCall const & x ) const
        {
            if( x.name_ == "pi" ){
                return M_PI;
            }

            if( x.name_ == "e" ){
                return std::exp( 1.0 );
            }

            double const arg = boost::apply_visitor( *this, x.arg_ );

            if( x.name_ == "sin" ){
                return sin( arg );
            }

            if( x.name_ == "cos" ){
                return cos( arg );
            }

            if( x.name_ == "tan" ){
                return tan( arg );
            }

            if( x.name_ == "abs" ){
                return fabs( arg );
            }

            if( x.name_ == "rad" ){
                return arg * 2 * M_PI / 360;
            }

            if( x.name_ == "deg" ){
                return arg * 360 / 2 / M_PI;
            }

            if( x.name_ == "log" ){
                return log( arg );
            }

            if( x.name_ == "log10" ){
                return log10( arg );
            }

            if( x.name_ == "log2" ){
                return log2( arg );
            }

            return 0;
        }

        double operator()( Expression const & x ) const
        {
            double state = boost::apply_visitor( *this, x.head_ );

            for( Operation const & oper : x.tail_ )
            {
                state = ( *this )( oper, state );
            }

            return state;
        }

    private:

        double args_[ 10 ];
    };
}

namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
using boost::phoenix::function;

struct error_handler_
{
    template< typename, typename, typename >
    struct result { typedef void type; };

    template< typename TIterator >
    void operator()(
        qi::info const & what
      , TIterator err_pos, TIterator last ) const
    {
        std::cout
            << "Error! Expecting "
            << what
            << " here: \""
            << std::string( err_pos, last )
            << "\""
            << std::endl
        ;
    }
};

function< error_handler_ > const error_handler = error_handler_();

template< typename TIterator >
struct Calculator
    : qi::grammar< TIterator, ast::Expression(), ascii::space_type >
{
    Calculator()
        : Calculator::base_type( expression )
    {
        qi::char_type char_;
        qi::digit_type digit_;
        qi::double_type double_;
        qi::lit_type lit_;
        qi::string_type string_;
        qi::alpha_type alpha_;
        qi::alnum_type alnum_;

        using qi::on_error;
        using qi::fail;
        using qi::lexeme;

        expression =
            relationalExpression
            >> *(   (string_( "&&" ) > relationalExpression )
                |   (string_( "||" ) > relationalExpression )
                )
            ;

        relationalExpression =
            additiveExpression
            >> *(   (string_( "<=" ) > additiveExpression )
                |   (string_( "<" ) > additiveExpression )
                |   (string_( ">=" ) > additiveExpression )
                |   (string_( ">" ) > additiveExpression )
                |   (string_( "!=" ) > additiveExpression )
                |   (string_( "==" ) > additiveExpression )
                )
            ;

        additiveExpression =
            multiplicativeExpression
            >> *(   (char_( '+' ) > multiplicativeExpression )
                |   (char_( '-' ) > multiplicativeExpression )
                )
            ;

        multiplicativeExpression =
            exponentialExpression
            >> *(   ( char_( '*' ) > exponentialExpression )
                |   ( char_( '/' ) > exponentialExpression )
                )
            ;

        exponentialExpression =
            unaryExpression
            >> -( '^' >> exponentialExpression );

        unaryExpression =
                ( char_( '-' ) > unaryExpression )
            |   ( char_( '+' ) > unaryExpression )
            |   ( char_( '!' ) > unaryExpression )
            |   primaryExpression
            ;

        primaryExpression =
                double_
            |   arg
            |   functionCall
            |   '(' > expression > ')'
            ;

        arg =
            ( '_' > digit_ )
            ;

        functionCall =
            ( +alnum_ > '(' > -unaryExpression > ')' )
            ;

        BOOST_SPIRIT_DEBUG_NODE( expression );
        BOOST_SPIRIT_DEBUG_NODE( relationalExpression );
        BOOST_SPIRIT_DEBUG_NODE( additiveExpression );
        BOOST_SPIRIT_DEBUG_NODE( multiplicativeExpression );
        BOOST_SPIRIT_DEBUG_NODE( exponentialExpression );
        BOOST_SPIRIT_DEBUG_NODE( unaryExpression );
        BOOST_SPIRIT_DEBUG_NODE( primaryExpression );
        BOOST_SPIRIT_DEBUG_NODE( arg );
        BOOST_SPIRIT_DEBUG_NODE( functionCall );

        on_error< fail >(
            expression,
            error_handler( qi::_4_type(), qi::_3_type(), qi::_2_type() )
        );
    }

private:

    qi::rule< TIterator, ast::Expression(), ascii::space_type > expression;
    qi::rule< TIterator, ast::Expression(), ascii::space_type > relationalExpression;
    qi::rule< TIterator, ast::Expression(), ascii::space_type > additiveExpression;
    qi::rule< TIterator, ast::Expression(), ascii::space_type > multiplicativeExpression;
    qi::rule< TIterator, ast::RightAssocExpr(), ascii::space_type > exponentialExpression;
    qi::rule< TIterator, ast::Operand(), ascii::space_type > unaryExpression;
    qi::rule< TIterator, ast::Operand(), ascii::space_type > primaryExpression;
    qi::rule< TIterator, ast::Arg(), ascii::space_type > arg;
    qi::rule< TIterator, ast::FunctionCall(), ascii::space_type > functionCall;
};

template< typename... TDoubles>
double eval(
    std::string const & expr,
    TDoubles... ds
)
{
    std::string::const_iterator iter = expr.begin();
    std::string::const_iterator end = expr.end();

    Calculator< std::string::const_iterator > calc;

    boost::spirit::ascii::space_type space;

    ast::Expression expression;

    bool const r = phrase_parse( iter, end, calc, space, expression );

    if( r && iter == end )
    {
        ast::Eval eval( std::forward< TDoubles >( ds )... );
        return eval( expression );
    }

    throw std::runtime_error( "Parsing failed: " + std::string( iter, end ) );
}

int main( int argc, char* argv[] )
{
    using std::cout;
    using std::endl;
    using std::stof;

    try
    {
        if( argc == 2 ){
            cout << eval( argv[1] ) << endl;
        }
        else if( argc == 3 ){
            cout << eval( argv[1], stof(argv[2]) ) << endl;
        }
        else if( argc == 4 ){
            cout << eval( argv[1], stof(argv[2]), stof(argv[3]) ) << endl;
        }
        else if( argc == 5 ){
            cout << eval( argv[1], stof(argv[2]), stof(argv[3]),
                stof(argv[4]) ) << endl;
        }
        else if( argc == 6 ){
            cout << eval( argv[1], stof(argv[2]), stof(argv[3]),
                stof(argv[4]), stof(argv[5]) ) << endl;
        }
        else if( argc == 7 ){
            cout << eval( argv[1], stof(argv[2]), stof(argv[3]),
                stof(argv[4]), stof(argv[5]), stof(argv[6]) ) << endl;
        }
        else if( argc == 8 ){
            cout << eval( argv[1], stof(argv[2]), stof(argv[3]),
                stof(argv[4]), stof(argv[5]), stof(argv[6]),
                stof(argv[7]) ) << endl;
        }
        else if( argc == 9 ){
            cout << eval( argv[1], stof(argv[2]), stof(argv[3]),
                stof(argv[4]), stof(argv[5]), stof(argv[6]),
                stof(argv[7]), stof(argv[8]) ) << endl;
        }
        else if( argc == 10 ){
            cout << eval( argv[1], stof(argv[2]), stof(argv[3]),
                stof(argv[4]), stof(argv[5]), stof(argv[6]),
                stof(argv[7]), stof(argv[8]), stof(argv[9]) ) << endl;
        }
        else if( argc == 11 ){
            cout << eval( argv[1], stof(argv[2]), stof(argv[3]),
                stof(argv[4]), stof(argv[5]), stof(argv[6]),
                stof(argv[7]), stof(argv[8]), stof(argv[9]),
                stof(argv[10]) ) << endl;
        }
    }
    catch( std::runtime_error const & e ){
        std::cerr << e.what() << std::endl;
    }
}

<commit_msg>Small fixed + unit tests<commit_after>// Define this to enable debugging
// #define BOOST_SPIRIT_QI_DEBUG

#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/variant/recursive_variant.hpp>
#include <boost/variant/apply_visitor.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/spirit/include/phoenix_function.hpp>
#include <boost/optional.hpp>

#include <iostream>
#include <string>

namespace ast
{
    struct Nil;

    struct Signed;

    struct Expression;

    struct RightAssocExpr;

    struct Arg;

    struct FunctionCall;

    typedef boost::variant<
            Nil
          , double
          , boost::recursive_wrapper< Signed >
          , boost::recursive_wrapper< Expression >
          , boost::recursive_wrapper< RightAssocExpr >
          , boost::recursive_wrapper< Arg >
          , boost::recursive_wrapper< FunctionCall >
        >
    Operand;

    struct Nil
    {
    };

    inline std::ostream & operator<<( std::ostream & out, Nil ) { out << "nil"; return out; }

    struct Signed
    {
        char sign_;
        Operand operand_;
    };

    struct Arg
    {
        char digit_;
    };

    struct RightAssocExpr
    {
        Operand left_;
        boost::optional< Operand > right_;
    };

    struct FunctionCall
    {
        std::string name_;
        Operand arg_;
    };

    struct Operation
    {
        std::string operator_;
        Operand operand_;
    };

    struct Expression
    {
        Operand head_;
        std::list< Operation > tail_;
    };

}

BOOST_FUSION_ADAPT_STRUCT(
    ast::Signed,
    ( char, sign_ )
    ( ast::Operand, operand_ )
)

BOOST_FUSION_ADAPT_STRUCT(
    ast::Arg,
    ( char, digit_ )
)

BOOST_FUSION_ADAPT_STRUCT(
    ast::RightAssocExpr,
    ( ast::Operand, left_ )
    ( boost::optional< ast::Operand >, right_ )
)

BOOST_FUSION_ADAPT_STRUCT(
    ast::FunctionCall,
    ( std::string, name_ )
    ( ast::Operand, arg_ )
)

BOOST_FUSION_ADAPT_STRUCT(
    ast::Operation,
    ( std::string, operator_ )
    ( ast::Operand, operand_ )
)

BOOST_FUSION_ADAPT_STRUCT(
    ast::Expression,
    ( ast::Operand, head_ )
    ( std::list< ast::Operation >, tail_ )
)

namespace ast
{
    struct Calc
    {
    public:

        typedef double result_type;

    private:

        template< typename... TDoubles >
        void assign( double * args, int index, double head, TDoubles &&... tail )
        {
            args[ index ] = head;
            assign( args, index+1, std::forward< TDoubles >( tail )... );
        }

        void assign( double * args, int index )
        {
        }

    public:

        template< typename... Doubles >
        Calc( Doubles &&... ds )
        {
            static_assert( sizeof...( Doubles ) < 10, "sizeof...(Doubles) has to be less then 10" );
            args_[ 0 ] = sizeof...( Doubles );

            assign( & args_[ 0 ], 1, std::forward< Doubles >( ds )... );
        }

        double operator()( Nil ) const
        {
            BOOST_ASSERT( 0 ); return 0;
        }

        double operator()( double n ) const
        {
            return n;
        }

        double operator()( Operation const & x, double lhs ) const
        {
            if( x.operator_ == "&&" )
            {
                if( lhs > 0.000001 ){
                    return boost::apply_visitor( *this, x.operand_ );
                }
                else{
                    return 0;
                }
            }

            if( x.operator_ == "||" )
            {
                if( lhs > 0.000001 ){
                    return lhs;
                }
                else{
                    return boost::apply_visitor( *this, x.operand_ );
                }
            }

            double const rhs = boost::apply_visitor( *this, x.operand_ );

            if( x.operator_ == "+" ){
                return lhs + rhs;
            }

            if( x.operator_ == "-" ){
                return lhs - rhs;
            }

            if( x.operator_ == "*" ){
                return lhs * rhs;
            }

            if( x.operator_ == "/" ){
                return lhs / rhs;
            }

            if( x.operator_ == "<" ){
                return lhs < rhs;
            }

            if( x.operator_ == "<=" ){
                return lhs <= rhs;
            }

            if( x.operator_ == ">" ){
                return lhs > rhs;
            }

            if( x.operator_ == ">=" ){
                return lhs >= rhs;
            }

            if( x.operator_ == "==" ){
                return lhs == rhs;
            }

            if( x.operator_ == "!=" ){
                return lhs != rhs;
            }

            return 0;
        }

        double operator()( Signed const & x ) const
        {
            double const rhs = boost::apply_visitor( *this, x.operand_ );

            switch( x.sign_ )
            {
                case '-': return -rhs;
                case '+': return +rhs;
                case '!': return ! (bool)rhs;
                default: BOOST_ASSERT( 0 );
            }
        }

        double operator()( Arg const & x ) const
        {
            return args_[ x.digit_ - '0' ];
        }

        double operator()( RightAssocExpr const & x ) const
        {
            double const base = boost::apply_visitor( *this, x.left_ );

            if( ! x.right_ ){
                return base;
            }

            double const exp = boost::apply_visitor( *this, x.right_.get() );

            return std::pow( base, exp );
        }

        double operator()( FunctionCall const & x ) const
        {
            if( x.name_ == "pi" ){
                return M_PI;
            }

            if( x.name_ == "e" ){
                return std::exp( 1.0 );
            }

            double const arg = boost::apply_visitor( *this, x.arg_ );

            if( x.name_ == "sin" ){
                return sin( arg );
            }

            if( x.name_ == "cos" ){
                return cos( arg );
            }

            if( x.name_ == "tan" ){
                return tan( arg );
            }

            if( x.name_ == "abs" ){
                return fabs( arg );
            }

            if( x.name_ == "rad" ){
                return arg * 2 * M_PI / 360;
            }

            if( x.name_ == "deg" ){
                return arg * 360 / 2 / M_PI;
            }

            if( x.name_ == "log" ){
                return log( arg );
            }

            if( x.name_ == "log10" ){
                return log10( arg );
            }

            if( x.name_ == "log2" ){
                return log2( arg );
            }

            return 0;
        }

        double operator()( Expression const & x ) const
        {
            double state = boost::apply_visitor( *this, x.head_ );

            for( Operation const & oper : x.tail_ )
            {
                state = ( *this )( oper, state );
            }

            return state;
        }

    private:

        double args_[ 10 ];
    };
}

namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
using boost::phoenix::function;

struct error_handler_
{
    template< typename, typename, typename >
    struct result { typedef void type; };

    template< typename TIterator >
    void operator()(
        qi::info const & what
      , TIterator err_pos, TIterator last ) const
    {
        std::cout
            << "Error! Expecting "
            << what
            << " here: \""
            << std::string( err_pos, last )
            << "\""
            << std::endl
        ;
    }
};

function< error_handler_ > const error_handler = error_handler_();

template< typename TIterator >
struct Calculator
    : qi::grammar< TIterator, ast::Expression(), ascii::space_type >
{
    Calculator()
        : Calculator::base_type( expression )
    {
        qi::char_type char_;
        qi::digit_type digit_;
        qi::double_type double_;
        qi::lit_type lit_;
        qi::string_type string_;
        qi::alpha_type alpha_;
        qi::alnum_type alnum_;

        using qi::on_error;
        using qi::fail;
        using qi::lexeme;

        expression =
            relationalExpression
            >> *(   (string_( "&&" ) > relationalExpression )
                |   (string_( "||" ) > relationalExpression )
                )
            ;

        relationalExpression =
            additiveExpression
            >> *(   (string_( "<=" ) > additiveExpression )
                |   (string_( "<" ) > additiveExpression )
                |   (string_( ">=" ) > additiveExpression )
                |   (string_( ">" ) > additiveExpression )
                |   (string_( "!=" ) > additiveExpression )
                |   (string_( "==" ) > additiveExpression )
                )
            ;

        additiveExpression =
            multiplicativeExpression
            >> *(   (char_( '+' ) > multiplicativeExpression )
                |   (char_( '-' ) > multiplicativeExpression )
                )
            ;

        multiplicativeExpression =
            exponentialExpression
            >> *(   ( char_( '*' ) > exponentialExpression )
                |   ( char_( '/' ) > exponentialExpression )
                )
            ;

        exponentialExpression =
            unaryExpression
            >> -( '^' >> exponentialExpression );

        unaryExpression =
                ( char_( '-' ) > unaryExpression )
            |   ( char_( '+' ) > unaryExpression )
            |   ( char_( '!' ) > unaryExpression )
            |   primaryExpression
            ;

        primaryExpression =
                double_
            |   arg
            |   functionCall
            |   '(' > expression > ')'
            ;

        arg =
            ( '_' > digit_ )
            ;

        functionCall =
            ( +alnum_ > '(' > -unaryExpression > ')' )
            ;

        BOOST_SPIRIT_DEBUG_NODE( expression );
        BOOST_SPIRIT_DEBUG_NODE( relationalExpression );
        BOOST_SPIRIT_DEBUG_NODE( additiveExpression );
        BOOST_SPIRIT_DEBUG_NODE( multiplicativeExpression );
        BOOST_SPIRIT_DEBUG_NODE( exponentialExpression );
        BOOST_SPIRIT_DEBUG_NODE( unaryExpression );
        BOOST_SPIRIT_DEBUG_NODE( primaryExpression );
        BOOST_SPIRIT_DEBUG_NODE( arg );
        BOOST_SPIRIT_DEBUG_NODE( functionCall );

        on_error< fail >(
            expression,
            error_handler( qi::_4_type(), qi::_3_type(), qi::_2_type() )
        );
    }

private:

    qi::rule< TIterator, ast::Expression(), ascii::space_type > expression;
    qi::rule< TIterator, ast::Expression(), ascii::space_type > relationalExpression;
    qi::rule< TIterator, ast::Expression(), ascii::space_type > additiveExpression;
    qi::rule< TIterator, ast::Expression(), ascii::space_type > multiplicativeExpression;
    qi::rule< TIterator, ast::RightAssocExpr(), ascii::space_type > exponentialExpression;
    qi::rule< TIterator, ast::Operand(), ascii::space_type > unaryExpression;
    qi::rule< TIterator, ast::Operand(), ascii::space_type > primaryExpression;
    qi::rule< TIterator, ast::Arg(), ascii::space_type > arg;
    qi::rule< TIterator, ast::FunctionCall(), ascii::space_type > functionCall;
};

template< typename... TDoubles>
double calc(
    std::string const & expr,
    TDoubles... ds
)
{
    std::string::const_iterator iter = expr.begin();
    std::string::const_iterator end = expr.end();

    Calculator< std::string::const_iterator > calc;

    boost::spirit::ascii::space_type space;

    ast::Expression expression;

    bool const r = phrase_parse( iter, end, calc, space, expression );

    if( r && iter == end )
    {
        ast::Calc calc( std::forward< TDoubles >( ds )... );
        return calc( expression );
    }

    throw std::runtime_error( "Parsing failed: " + std::string( iter, end ) );
}

void test()
{
    assert( calc( "1+2*3" ) == 7 );
    assert( calc( "2^3^2" ) == 512 );
    assert( calc( "(1+2)*(3+4)" ) == 21 );

    assert( calc( "1+-1" ) == 0 );
    assert( calc( "1--1" ) == 2 );

    assert( calc( "4/2" ) == 2 );
    assert( calc( "4.5/2" ) == 2.25 );
    assert( calc( "5/2" ) == 2.5 );

    assert( calc( "pi()" ) == double(M_PI) );
    assert( calc( "e()" ) == std::exp( 1.0 ) );

    assert( calc( "12||(1/0)" ) == 12 );
    assert( calc( "0&&(1/0)" ) == 0 );
    assert( calc( "1&&2" ) == 2 );
    assert( calc( "2||1" ) == 2 );
    assert( calc( "0||2" ) == 2 );

    assert( calc( "1<2" ) == 1 );
    assert( calc( "2<1" ) == 0 );

    assert( calc( "1>2" ) == 0 );
    assert( calc( "2>1" ) == 1 );

    assert( calc( "1<=2" ) == 1 );
    assert( calc( "2<=2" ) == 1 );
    assert( calc( "3<=2" ) == 0 );

    assert( calc( "1>=2" ) == 0 );
    assert( calc( "2>=2" ) == 1 );
    assert( calc( "3>=2" ) == 1 );

    assert( calc( "abs(1)" ) == 1 );
    assert( calc( "abs(-1)" ) == 1 );

    assert( calc( "sin(rad(0))" ) == 0 );
    assert(( fabs( calc( "sin(rad(90))" ) - 1.0 ) < 0.000001 ));

    assert( calc( "cos(rad(0))" ) == 1 );
    assert(( fabs( calc( "cos(rad(90))" ) - 0.0 ) < 0.0000001 ));

    assert( calc( "log10(1)" ) == 0 );
    assert( calc( "log10(10)" ) == 1 );
    assert( calc( "log10(100)" ) == 2 );

    assert( calc( "log2(1)" ) == 0 );
    assert( calc( "log2(2)" ) == 1 );
    assert( calc( "log2(4)" ) == 2 );

    assert( calc( "_1+_2+_3+_4+_5+_6+_7+_8+_9", 1, 2, 3, 4, 5, 6, 7, 8, 9 ) == 45 );
    assert( calc( "_0" ) == 0 );
    assert( calc( "_0", 1 ) == 1 );
    assert( calc( "_0", 1, 2 ) == 2 );
    assert( calc( "_0", 1, 2, 3 ) == 3 );
}

int main( int argc, char* argv[] )
{
    test();

    using std::cout;
    using std::endl;
    using std::stof;

    try
    {
        if( argc == 2 ){
            cout << calc( argv[1] ) << endl;
        }
        else if( argc == 3 ){
            cout << calc( argv[1], stof(argv[2]) ) << endl;
        }
        else if( argc == 4 ){
            cout << calc( argv[1], stof(argv[2]), stof(argv[3]) ) << endl;
        }
        else if( argc == 5 ){
            cout << calc( argv[1], stof(argv[2]), stof(argv[3]),
                stof(argv[4]) ) << endl;
        }
        else if( argc == 6 ){
            cout << calc( argv[1], stof(argv[2]), stof(argv[3]),
                stof(argv[4]), stof(argv[5]) ) << endl;
        }
        else if( argc == 7 ){
            cout << calc( argv[1], stof(argv[2]), stof(argv[3]),
                stof(argv[4]), stof(argv[5]), stof(argv[6]) ) << endl;
        }
        else if( argc == 8 ){
            cout << calc( argv[1], stof(argv[2]), stof(argv[3]),
                stof(argv[4]), stof(argv[5]), stof(argv[6]),
                stof(argv[7]) ) << endl;
        }
        else if( argc == 9 ){
            cout << calc( argv[1], stof(argv[2]), stof(argv[3]),
                stof(argv[4]), stof(argv[5]), stof(argv[6]),
                stof(argv[7]), stof(argv[8]) ) << endl;
        }
        else if( argc == 10 ){
            cout << calc( argv[1], stof(argv[2]), stof(argv[3]),
                stof(argv[4]), stof(argv[5]), stof(argv[6]),
                stof(argv[7]), stof(argv[8]), stof(argv[9]) ) << endl;
        }
        else if( argc == 11 ){
            cout << calc( argv[1], stof(argv[2]), stof(argv[3]),
                stof(argv[4]), stof(argv[5]), stof(argv[6]),
                stof(argv[7]), stof(argv[8]), stof(argv[9]),
                stof(argv[10]) ) << endl;
        }
    }
    catch( std::runtime_error const & e ){
        std::cerr << e.what() << std::endl;
    }
}

<|endoftext|>


<commit_before>// coding: utf-8
#include "hardware.hpp"

// system tick timer
ISR(TIMER2_COMPA_vect)
{
	xpcc::Clock::increment();
}

// main function \o/
int
main(void)
{
	// set the led pins
	initializeLeds();
	RedLedPin::setOutput();
	GreenLedPin::setOutput();
	BlueLedPin::setOutput();

	LightSensorPin::setInput();

	// set up the oversampling of the photosensitive diode
	AdcInterrupt::initialize(AdcInterrupt::Reference::ExternalARef, AdcInterrupt::Prescaler::Div32);
	lightSensor::initialize(adcMap, &lightSensorData);

	GpioD0::connect(Uart::Rx);
	GpioD1::connect(Uart::Tx);
	Uart::initialize<Uart::B115200>();

	xpcc::atmega::enableInterrupts();
	XPCC_LOG_INFO << "\n\nRESTART\n\n";

	while(1)
	{
		dreamer.run();
	}

	return 0;
}
<commit_msg>Set Uart Baudrate to 38.4kBaud, for minimal error.<commit_after>// coding: utf-8
#include "hardware.hpp"

// system tick timer
ISR(TIMER2_COMPA_vect)
{
	xpcc::Clock::increment();
}

// main function \o/
int
main(void)
{
	// set the led pins
	initializeLeds();
	RedLedPin::setOutput();
	GreenLedPin::setOutput();
	BlueLedPin::setOutput();

	LightSensorPin::setInput();

	// set up the oversampling of the photosensitive diode
	AdcInterrupt::initialize(AdcInterrupt::Reference::ExternalARef, AdcInterrupt::Prescaler::Div32);
	lightSensor::initialize(adcMap, &lightSensorData);

	GpioD0::connect(Uart::Rx);
	GpioD1::connect(Uart::Tx);
	Uart::initialize<Uart::B38400>();

	xpcc::atmega::enableInterrupts();
	XPCC_LOG_INFO << "\n\nRESTART\n\n";

	while(1)
	{
		dreamer.run();
	}

	return 0;
}
<|endoftext|>


<commit_before>#include "ImageProvider.hpp"
#include "Resources.hpp"
#include <SDL.h>
#include <iostream>

#include <opencv2/imgproc/imgproc.hpp>

static const char *face_cascade_xml = "haarcascade_frontalface_alt.xml";
static const char *eyes_cascade_xml = "haarcascade_eye_tree_eyeglasses.xml";

void ImageData::resize(size_t width, size_t height) {
  webcam_pixels = cv::Mat::zeros(height, width, CV_8UC3);
}

ImageProvider::ImageProvider()
    : AsyncMostRecentDataStream<ImageProvider, ImageData>() {}

static cv::Size getFrameSize(cv::VideoCapture &capture) {
#if CV_VERSION_EPOCH < 3
  int w = capture.get(CV_CAP_PROP_FRAME_WIDTH);
  int h = capture.get(CV_CAP_PROP_FRAME_HEIGHT);
#else
  int w = capture.get(cv::CAP_PROP_FRAME_WIDTH);
  int h = capture.get(cv::CAP_PROP_FRAME_HEIGHT);
#endif
  return {w, h};
}

bool ImageProvider::onBeforeStart() {
  capture.open(0);
  if (capture.isOpened()) {
    webcam_size = getFrameSize(capture);
    if (webcam_size.width > 0 && webcam_size.height > 0) {
      update_proto([this](ImageData &b) {
        b.resize(webcam_size.width, webcam_size.height);
      });
      face_cascade.load(resources->resolve(face_cascade_xml));
      (void)eyes_cascade_xml; // fix unused variable warning
      return true;
    }
  }
  SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Could not open webcam",
                           "Could not open webcam", NULL);
  return false;
}
void ImageProvider::onAfterStop() { capture.release(); }

void ImageProvider::produce(ImageData &assigned) {
  auto &frame = assigned.webcam_pixels;
  auto &faces = assigned.faces;

  if (!capture.isOpened()) {
    return;
  }
  if (!capture.read(frame)) {
    return;
  }
#ifndef NDEBUG
  {
    auto S = getFrameSize(capture);
    assert(static_cast<int>(S.width) == frame.cols &&
           static_cast<int>(S.height) == frame.rows);
  }
#endif

  cv::Mat frame_gray;

  cv::cvtColor(frame, frame_gray, CV_BGR2GRAY);
  cv::equalizeHist(frame_gray, frame_gray);
  face_cascade.detectMultiScale(frame_gray, faces, 1.1, 2,
                                0 | CV_HAAR_SCALE_IMAGE, cv::Size(30, 30));
  if (faces.size() > 0) {
    std::cout << "Detected faces: " << faces.size() << "\n";
  }
}
<commit_msg>Remove "Detected faces" debug output<commit_after>#include "ImageProvider.hpp"
#include "Resources.hpp"
#include <SDL.h>
#include <iostream>

#include <opencv2/imgproc/imgproc.hpp>

static const char *face_cascade_xml = "haarcascade_frontalface_alt.xml";
static const char *eyes_cascade_xml = "haarcascade_eye_tree_eyeglasses.xml";

void ImageData::resize(size_t width, size_t height) {
  webcam_pixels = cv::Mat::zeros(height, width, CV_8UC3);
}

ImageProvider::ImageProvider()
    : AsyncMostRecentDataStream<ImageProvider, ImageData>() {}

static cv::Size getFrameSize(cv::VideoCapture &capture) {
#if CV_VERSION_EPOCH < 3
  int w = capture.get(CV_CAP_PROP_FRAME_WIDTH);
  int h = capture.get(CV_CAP_PROP_FRAME_HEIGHT);
#else
  int w = capture.get(cv::CAP_PROP_FRAME_WIDTH);
  int h = capture.get(cv::CAP_PROP_FRAME_HEIGHT);
#endif
  return {w, h};
}

bool ImageProvider::onBeforeStart() {
  capture.open(0);
  if (capture.isOpened()) {
    webcam_size = getFrameSize(capture);
    if (webcam_size.width > 0 && webcam_size.height > 0) {
      update_proto([this](ImageData &b) {
        b.resize(webcam_size.width, webcam_size.height);
      });
      face_cascade.load(resources->resolve(face_cascade_xml));
      (void)eyes_cascade_xml; // fix unused variable warning
      return true;
    }
  }
  SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Could not open webcam",
                           "Could not open webcam", NULL);
  return false;
}
void ImageProvider::onAfterStop() { capture.release(); }

void ImageProvider::produce(ImageData &assigned) {
  auto &frame = assigned.webcam_pixels;
  auto &faces = assigned.faces;

  if (!capture.isOpened()) {
    return;
  }
  if (!capture.read(frame)) {
    return;
  }
#ifndef NDEBUG
  {
    auto S = getFrameSize(capture);
    assert(static_cast<int>(S.width) == frame.cols &&
           static_cast<int>(S.height) == frame.rows);
  }
#endif

  cv::Mat frame_gray;

  cv::cvtColor(frame, frame_gray, CV_BGR2GRAY);
  cv::equalizeHist(frame_gray, frame_gray);
  face_cascade.detectMultiScale(frame_gray, faces, 1.1, 2,
                                0 | CV_HAAR_SCALE_IMAGE, cv::Size(30, 30));
}
<|endoftext|>


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

#include "condor_common.h"
#include "condor_debug.h"
#include "process_control.WINDOWS.h"
#include "ntsysinfo.h"
#include "HashTable.h"
#include "condor_softkill.h"

static char* soft_kill_binary = NULL;

void
set_windows_soft_kill_binary(char* binary)
{
	if (soft_kill_binary != NULL) {
		free(soft_kill_binary);
	}
	soft_kill_binary = strdup(binary);
	ASSERT(soft_kill_binary != NULL);
}

bool
windows_soft_kill(DWORD pid)
{
	// softkill on Windows is implemented by posting a WM_CLOSE to
	// a top-level window owned by the process (if the process owns
	// more than 1 top-level window, it is undefined which one will
	// get the WM_CLOSE). finding the window and posting the message
	// are done using the condor_softkill tool
	//

	// bail out if the binary wasn't set
	//
	if (soft_kill_binary == NULL) {
		dprintf(D_ALWAYS,
		        "windows_soft_kill error: softkill binary not set\n");
		return false;
	}

	// construct the command line for the call to CreateProcess
	//
	char command_line[1024];
	command_line[1023] = '\0';
	int ret = snprintf(command_line,
	                   1024,
	                   "\"%s\" %u debug.txt",
	                   soft_kill_binary,
					   pid);
	if (ret < 0) {
		dprintf(D_ALWAYS,
		        "windows:soft_kill: snprintf error: %s (%d)\n",
		        strerror(errno),
		        errno);
		return false;
	}
	if (command_line[1023] != '\0') {
		dprintf(D_ALWAYS,
		        "windows_soft_kill: command line too long\n");
		return false;
	}

	// call CreateProcess to run the softkill executable
	//
	STARTUPINFO si;
	memset(&si, 0, sizeof(STARTUPINFO));
	PROCESS_INFORMATION pi;
	if (CreateProcess(NULL,         // get the executable from the command line
	                  command_line,
	                  NULL,         // default process security
	                  NULL,         // default security for primary thread
	                  FALSE,        // don't inherit handles
	                  0,            // no flags
	                  NULL,         // inherit our environment
	                  NULL,         // inherit our working directory
	                  &si,
	                  &pi) == FALSE)
	{
		dprintf(D_ALWAYS,
		        "windows_soft_kill: CreateProcess error: %u\n",
		        GetLastError());
		return false;
	}
	CloseHandle(pi.hThread);

	// wait for the exit code; fail if we can't get it or if
	// it indicates failure
	//
	if (WaitForSingleObject(pi.hProcess, INFINITE) != WAIT_OBJECT_0) {
		dprintf(D_ALWAYS,
		        "windows_soft_kill: WaitForSingleObject error: %u\n",
		        GetLastError());
	}
	DWORD exit_code;
	BOOL result = GetExitCodeProcess(pi.hProcess, &exit_code);
	DWORD error = GetLastError();
	CloseHandle(pi.hProcess);
	if (result == FALSE) {
		dprintf(D_ALWAYS,
		        "windows_soft_kill: GetExitCodeProcess error: %u\n",
		        GetLastError());
		return false;
	}
	if (exit_code != SOFTKILL_SUCCESS) {
		dprintf(D_ALWAYS,
		        "windows_soft_kill: error code from condor_softkill: %u\n",
		        exit_code);
		return false;
	}

	// i guess it worked
	//
	return true;
}

bool
windows_hard_kill(DWORD pid)
{
	HANDLE process = OpenProcess(PROCESS_TERMINATE, FALSE, pid);
	if (process == NULL) {
		dprintf(D_ALWAYS,
		        "windows_hard_kill: OpenProcess error: %u\n",
		        GetLastError());
		return false;
	}
	if (TerminateProcess(process, 0) == FALSE) {
		dprintf(D_ALWAYS,
		        "windows_hard_kill: TerminateProcess error: %u\n",
		        GetLastError());
		return false;
	}
	CloseHandle(process);

	return true;
}

// stupid hash function for DWORD (need by the hash tables
// used in the suspend/contiunue code below)
//
static unsigned int
hash_func(const DWORD& n)
{
	return n;
}

// stupid object for getting the list of threads for a process
//
static CSysinfo sys_info;

// record to keep track of the threads in a suspended process
//
struct SuspendedThread {

	// cache a handle to each thread while the process is suspended
	//
	HANDLE handle;

	// how many suspends did we send this thread? (so we know how
	// many resumes to send!)
	//
	int num_suspends;
};

// for each suspended process we keep a hash table of its
// SuspendedThread structures (keyed on thread id)
//
typedef HashTable<DWORD, SuspendedThread*> SuspendedProcess;

// we keep track of all suspended processes via a hash table
// keyed on process id
//
static HashTable<DWORD, SuspendedProcess*> suspended_processes(11, hash_func);

bool
windows_suspend(DWORD pid)
{
	// make sure we haven't already suspended this process, then allocate
	// a SuspendedProcess object and add it to the hash table
	//
	SuspendedProcess* sp;
	int ret = suspended_processes.lookup(pid, sp);
	ASSERT(ret == -1);
	sp = new SuspendedProcess(11, hash_func);
	ASSERT(sp != NULL);
	ret = suspended_processes.insert(pid, sp);
	ASSERT(ret != -1);

	// we need to loop until iterating through the thread list for this
	// process shows that all threads are suspended
	//
	bool finished = false;
	while (!finished) {

		// assume we're going to succeed on this iteration until
		// proven otherwise
		//
		finished = true;

		// get the list of threads for this process
		//
		ExtArray<DWORD> tid_array;
		int num_tids = sys_info.GetTIDs(pid, tid_array);
		if (num_tids == 0) {
			// TODO: we need to handle this case!!!
			//
			EXCEPT("windows_suspend_process failed: can't get threads for pid %u",
			       pid);
		}

		// go through the thread list, calling SuspendThread on each
		//
		for (int i = 0; i < num_tids; i++) {

			// see if we already have a record for this thread
			//
			SuspendedThread* st;
			ret = sp->lookup(tid_array[i], st);
			if (ret == -1) {

				// no record yet; open up this thread's handle
				//
				HANDLE handle = OpenThread(THREAD_SUSPEND_RESUME, FALSE, tid_array[i]);
				if (handle == NULL) {

					// the thread probably exited; however, it may have called ResumeThread
					// or CreateThread first, so we'll have to loop again
					//
					finished = false;
					dprintf(D_FULLDEBUG,
					        "windows_suspend_process: OpenThread error: %u\n",
					        GetLastError());
					continue;
				}

				// now that we have a handle, create a record for this thread
				//
				st = new SuspendedThread;
				ASSERT(st != NULL);
				st->handle = handle;
				st->num_suspends = 0;
				ret = sp->insert(tid_array[i], st);
				ASSERT(ret != -1);
			}

			// now suspend the thread
			//
			DWORD result = SuspendThread(st->handle);
			if (result == (DWORD)-1) {

				// how can this happen? maybe the thread exited?
				//
				finished = false;
				dprintf(D_FULLDEBUG,
						"windows_suspend_process: SuspendThread error: %u\n",
						GetLastError());
				continue;
			}

			// if the thread was not suspended prior to our SuspendThread call,
			// it may have called CreateThread or ResumeThread so we need to loop
			// at least one more time
			//
			if (result == 0) {
				finished = false;
			}

			// keep track of how many suspends we've sent this thread
			//
			st->num_suspends++;
		}
	}

	return true;
}

bool
windows_continue(DWORD pid)
{
	// we should have a record from when we suspended this process
	//
	SuspendedProcess* sp;
	int ret = suspended_processes.lookup(pid, sp);
	ASSERT(ret != -1);

	SuspendedThread* st;
	sp->startIterations();
	while (sp->iterate(st)) {

		// call ResumeThread as many times as we called SuspendThread
		// for this thread above
		//
		for (int i = 0; i < st->num_suspends; i++) {
			if (ResumeThread(st->handle) == (DWORD)-1) {
				dprintf(D_FULLDEBUG,
				        "windows_continue_process: ResumeThread error: %u\n",
				        GetLastError());
			}
		}

		// close our handle to this thread
		//
		if (CloseHandle(st->handle) == FALSE) {
			dprintf(D_FULLDEBUG,
			        "windows_continue_process: CloseHandle error: %u\n",
			        GetLastError());
		}

		// clean up this SuspendedThread object
		//
		delete st;
	}

	// clean up the SuspendedProcess object and remove it from the
	// hash table of processes
	//
	delete sp;
	ret = suspended_processes.remove(pid);
	ASSERT(ret != -1);

	return true;
}
<commit_msg>Fix handle leak when hard killing a process fails on Windows<commit_after>/***************************************************************
 *
 * Copyright (C) 1990-2007, Condor Team, Computer Sciences Department,
 * University of Wisconsin-Madison, WI.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you
 * may not use this file except in compliance with the License.  You may
 * obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 ***************************************************************/

#include "condor_common.h"
#include "condor_debug.h"
#include "process_control.WINDOWS.h"
#include "ntsysinfo.h"
#include "HashTable.h"
#include "condor_softkill.h"

static char* soft_kill_binary = NULL;

void
set_windows_soft_kill_binary(char* binary)
{
	if (soft_kill_binary != NULL) {
		free(soft_kill_binary);
	}
	soft_kill_binary = strdup(binary);
	ASSERT(soft_kill_binary != NULL);
}

bool
windows_soft_kill(DWORD pid)
{
	// softkill on Windows is implemented by posting a WM_CLOSE to
	// a top-level window owned by the process (if the process owns
	// more than 1 top-level window, it is undefined which one will
	// get the WM_CLOSE). finding the window and posting the message
	// are done using the condor_softkill tool
	//

	// bail out if the binary wasn't set
	//
	if (soft_kill_binary == NULL) {
		dprintf(D_ALWAYS,
		        "windows_soft_kill error: softkill binary not set\n");
		return false;
	}

	// construct the command line for the call to CreateProcess
	//
	char command_line[1024];
	command_line[1023] = '\0';
	int ret = snprintf(command_line,
	                   1024,
	                   "\"%s\" %u debug.txt",
	                   soft_kill_binary,
					   pid);
	if (ret < 0) {
		dprintf(D_ALWAYS,
		        "windows:soft_kill: snprintf error: %s (%d)\n",
		        strerror(errno),
		        errno);
		return false;
	}
	if (command_line[1023] != '\0') {
		dprintf(D_ALWAYS,
		        "windows_soft_kill: command line too long\n");
		return false;
	}

	// call CreateProcess to run the softkill executable
	//
	STARTUPINFO si;
	memset(&si, 0, sizeof(STARTUPINFO));
	PROCESS_INFORMATION pi;
	if (CreateProcess(NULL,         // get the executable from the command line
	                  command_line,
	                  NULL,         // default process security
	                  NULL,         // default security for primary thread
	                  FALSE,        // don't inherit handles
	                  0,            // no flags
	                  NULL,         // inherit our environment
	                  NULL,         // inherit our working directory
	                  &si,
	                  &pi) == FALSE)
	{
		dprintf(D_ALWAYS,
		        "windows_soft_kill: CreateProcess error: %u\n",
		        GetLastError());
		return false;
	}
	CloseHandle(pi.hThread);

	// wait for the exit code; fail if we can't get it or if
	// it indicates failure
	//
	if (WaitForSingleObject(pi.hProcess, INFINITE) != WAIT_OBJECT_0) {
		dprintf(D_ALWAYS,
		        "windows_soft_kill: WaitForSingleObject error: %u\n",
		        GetLastError());
	}
	DWORD exit_code;
	BOOL result = GetExitCodeProcess(pi.hProcess, &exit_code);
	DWORD error = GetLastError();
	CloseHandle(pi.hProcess);
	if (result == FALSE) {
		dprintf(D_ALWAYS,
		        "windows_soft_kill: GetExitCodeProcess error: %u\n",
		        GetLastError());
		return false;
	}
	if (exit_code != SOFTKILL_SUCCESS) {
		dprintf(D_ALWAYS,
		        "windows_soft_kill: error code from condor_softkill: %u\n",
		        exit_code);
		return false;
	}

	// i guess it worked
	//
	return true;
}

bool
windows_hard_kill(DWORD pid)
{
	HANDLE process = OpenProcess(PROCESS_TERMINATE, FALSE, pid);
	if (process == NULL) {
		dprintf(D_ALWAYS,
		        "windows_hard_kill: OpenProcess error: %u\n",
		        GetLastError());
		return false;
	}
	BOOL ok = TerminateProcess(process, 0);
	DWORD err = GetLastError();
	CloseHandle(process);
	if (ok == FALSE) {
		dprintf(D_ALWAYS,
		        "windows_hard_kill: TerminateProcess error: %u\n",
		        (unsigned)err);
		return false;
	}
	return true;
}

// stupid hash function for DWORD (need by the hash tables
// used in the suspend/contiunue code below)
//
static unsigned int
hash_func(const DWORD& n)
{
	return n;
}

// stupid object for getting the list of threads for a process
//
static CSysinfo sys_info;

// record to keep track of the threads in a suspended process
//
struct SuspendedThread {

	// cache a handle to each thread while the process is suspended
	//
	HANDLE handle;

	// how many suspends did we send this thread? (so we know how
	// many resumes to send!)
	//
	int num_suspends;
};

// for each suspended process we keep a hash table of its
// SuspendedThread structures (keyed on thread id)
//
typedef HashTable<DWORD, SuspendedThread*> SuspendedProcess;

// we keep track of all suspended processes via a hash table
// keyed on process id
//
static HashTable<DWORD, SuspendedProcess*> suspended_processes(11, hash_func);

bool
windows_suspend(DWORD pid)
{
	// make sure we haven't already suspended this process, then allocate
	// a SuspendedProcess object and add it to the hash table
	//
	SuspendedProcess* sp;
	int ret = suspended_processes.lookup(pid, sp);
	ASSERT(ret == -1);
	sp = new SuspendedProcess(11, hash_func);
	ASSERT(sp != NULL);
	ret = suspended_processes.insert(pid, sp);
	ASSERT(ret != -1);

	// we need to loop until iterating through the thread list for this
	// process shows that all threads are suspended
	//
	bool finished = false;
	while (!finished) {

		// assume we're going to succeed on this iteration until
		// proven otherwise
		//
		finished = true;

		// get the list of threads for this process
		//
		ExtArray<DWORD> tid_array;
		int num_tids = sys_info.GetTIDs(pid, tid_array);
		if (num_tids == 0) {
			// TODO: we need to handle this case!!!
			//
			EXCEPT("windows_suspend_process failed: can't get threads for pid %u",
			       pid);
		}

		// go through the thread list, calling SuspendThread on each
		//
		for (int i = 0; i < num_tids; i++) {

			// see if we already have a record for this thread
			//
			SuspendedThread* st;
			ret = sp->lookup(tid_array[i], st);
			if (ret == -1) {

				// no record yet; open up this thread's handle
				//
				HANDLE handle = OpenThread(THREAD_SUSPEND_RESUME, FALSE, tid_array[i]);
				if (handle == NULL) {

					// the thread probably exited; however, it may have called ResumeThread
					// or CreateThread first, so we'll have to loop again
					//
					finished = false;
					dprintf(D_FULLDEBUG,
					        "windows_suspend_process: OpenThread error: %u\n",
					        GetLastError());
					continue;
				}

				// now that we have a handle, create a record for this thread
				//
				st = new SuspendedThread;
				ASSERT(st != NULL);
				st->handle = handle;
				st->num_suspends = 0;
				ret = sp->insert(tid_array[i], st);
				ASSERT(ret != -1);
			}

			// now suspend the thread
			//
			DWORD result = SuspendThread(st->handle);
			if (result == (DWORD)-1) {

				// how can this happen? maybe the thread exited?
				//
				finished = false;
				dprintf(D_FULLDEBUG,
						"windows_suspend_process: SuspendThread error: %u\n",
						GetLastError());
				continue;
			}

			// if the thread was not suspended prior to our SuspendThread call,
			// it may have called CreateThread or ResumeThread so we need to loop
			// at least one more time
			//
			if (result == 0) {
				finished = false;
			}

			// keep track of how many suspends we've sent this thread
			//
			st->num_suspends++;
		}
	}

	return true;
}

bool
windows_continue(DWORD pid)
{
	// we should have a record from when we suspended this process
	//
	SuspendedProcess* sp;
	int ret = suspended_processes.lookup(pid, sp);
	ASSERT(ret != -1);

	SuspendedThread* st;
	sp->startIterations();
	while (sp->iterate(st)) {

		// call ResumeThread as many times as we called SuspendThread
		// for this thread above
		//
		for (int i = 0; i < st->num_suspends; i++) {
			if (ResumeThread(st->handle) == (DWORD)-1) {
				dprintf(D_FULLDEBUG,
				        "windows_continue_process: ResumeThread error: %u\n",
				        GetLastError());
			}
		}

		// close our handle to this thread
		//
		if (CloseHandle(st->handle) == FALSE) {
			dprintf(D_FULLDEBUG,
			        "windows_continue_process: CloseHandle error: %u\n",
			        GetLastError());
		}

		// clean up this SuspendedThread object
		//
		delete st;
	}

	// clean up the SuspendedProcess object and remove it from the
	// hash table of processes
	//
	delete sp;
	ret = suspended_processes.remove(pid);
	ASSERT(ret != -1);

	return true;
}
<|endoftext|>


<commit_before>#pragma once

#include <agency/detail/config.hpp>
#include <agency/cuda/detail/feature_test.hpp>
#include <agency/cuda/detail/terminate.hpp>
#include <agency/cuda/detail/launch_kernel.hpp>
#include <agency/cuda/detail/kernel.hpp>
#include <agency/cuda/detail/future/stream.hpp>
#include <agency/cuda/gpu.hpp>

namespace agency
{
namespace cuda
{
namespace detail
{

class event
{
  public:
    struct construct_ready_t {};
    static constexpr construct_ready_t construct_ready{};

  private:
    static constexpr int event_create_flags = cudaEventDisableTiming;

    __host__ __device__
    event(construct_ready_t, detail::stream&& s)
      : stream_(std::move(s))
    {
#if __cuda_lib_has_cudart
      detail::throw_on_error(cudaEventCreateWithFlags(&e_, event_create_flags), "cudaEventCreateWithFlags in cuda::detail::event ctor");
#else
      detail::terminate_with_message("cuda::detail::event ctor requires CUDART");
#endif
    }

    // constructs a new event recorded on the given stream
    __host__ __device__
    event(detail::stream&& s) : event(construct_ready, std::move(s))
    {
#if __cuda_lib_has_cudart
      detail::throw_on_error(cudaEventRecord(e_, stream().native_handle()), "cudaEventRecord in cuda::detail::event ctor");
#else
      detail::terminate_with_message("cuda::detail::event ctor requires CUDART");
#endif
    }

  public:
    // creates an invalid event
    __host__ __device__
    event() : stream_(), e_{0}
    {}

    __host__ __device__
    event(construct_ready_t)
      : event(construct_ready, detail::stream())
    {}

    __host__ __device__
    event(const event&) = delete;

    __host__ __device__
    event(event&& other)
      : stream_(std::move(other.stream())),
        e_(other.e_)
    {
      other.e_ = 0;
    }

    __host__ __device__
    ~event()
    {
      if(valid())
      {
        destroy_event();
      }
    }

    __host__ __device__
    bool valid() const
    {
      return e_ != 0;
    }

    __host__ __device__
    bool is_ready() const
    {
      if(valid())
      {
#if __cuda_lib_has_cudart
        cudaError_t result = cudaEventQuery(e_);

        if(result != cudaErrorNotReady && result != cudaSuccess)
        {
          detail::throw_on_error(result, "cudaEventQuery in cuda::detail::event::is_ready");
        }

        return result == cudaSuccess;
#else
        detail::terminate_with_message("cuda::detail::event::is_ready requires CUDART");
#endif
      }

      return false;
    }

    __host__ __device__
    void wait() const
    {
      // XXX should probably check for valid() here

#if __cuda_lib_has_cudart

#ifndef __CUDA_ARCH__
      detail::throw_on_error(cudaEventSynchronize(e_), "cudaEventSynchronize in cuda::detail::event::wait");
#else
      detail::throw_on_error(cudaDeviceSynchronize(), "cudaDeviceSynchronize in cuda::detail::event::wait");
#endif // __CUDA_ARCH__

#else
      detail::terminate_with_message("cuda::detail::event::wait requires CUDART");
#endif // __cuda_lib_has_cudart
    }

    __host__ __device__
    void swap(event& other)
    {
      stream().swap(other.stream());

      cudaEvent_t tmp = e_;
      e_ = other.e_;
      other.e_ = tmp;
    }

    template<class Function, class... Args>
    __host__ __device__
    static void *then_kernel()
    {
      return reinterpret_cast<void*>(&cuda_kernel<Function,Args...>);
    }

    template<class Function, class... Args>
    __host__ __device__
    static void *then_kernel(const Function&, const Args&...)
    {
      return then_kernel<Function,Args...>();
    }

    // this form of then() leaves this event in a valid state afterwards
    template<class Function, class... Args>
    __host__ __device__
    event then(Function f, dim3 grid_dim, dim3 block_dim, int shared_memory_size, const Args&... args)
    {
      // create a stream for the kernel
      detail::stream result_stream;

      // make the result_stream wait on this event before further launches
      stream_wait(result_stream, *this);

      // get the address of the kernel
      auto kernel = then_kernel(f,args...);

      // launch the kernel on the result's stream
      detail::checked_launch_kernel(kernel, grid_dim, block_dim, shared_memory_size, result_stream.native_handle(), f, args...);

      // return a new event
      return event(std::move(result_stream));
    }

    // this form of then() leaves this event in an invalid state afterwards
    template<class Function, class... Args>
    __host__ __device__
    event then_and_invalidate(Function f, dim3 grid_dim, dim3 block_dim, int shared_memory_size, const Args&... args)
    {
      // make the stream wait on this event before further launches
      stream_wait_and_invalidate();

      // get the address of the kernel
      auto kernel = then_kernel(f,args...);

      // launch the kernel on this event's stream
      detail::checked_launch_kernel(kernel, grid_dim, block_dim, shared_memory_size, stream().native_handle(), f, args...);

      // return a new event
      return event(std::move(stream()));
    }

    template<class Function, class... Args>
    __host__ __device__
    static void *then_on_kernel()
    {
      return reinterpret_cast<void*>(&cuda_kernel<Function,Args...>);
    }

    template<class Function, class... Args>
    __host__ __device__
    static void *then_on_kernel(const Function&, const Args&...)
    {
      return then_on_kernel<Function,Args...>();
    }

    // this form of then_on() leaves this event in an invalid state afterwards
    template<class Function, class... Args>
    __host__ __device__
    event then_on_and_invalidate(Function f, dim3 grid_dim, dim3 block_dim, int shared_memory_size, const gpu_id& gpu, const Args&... args)
    {
      // make the stream wait on this event before further launches
      stream_wait_and_invalidate();

      // get the address of the kernel
      auto kernel = then_on_kernel(f,args...);

      // if gpu differs from this event's stream's gpu, we need to create a new one for the launch
      // otherwise, just reuse this event's stream
      detail::stream new_stream = (gpu == stream().gpu()) ? std::move(stream()) : detail::stream(gpu);

      // launch the kernel on the new stream
      detail::checked_launch_kernel_on_device(kernel, grid_dim, block_dim, shared_memory_size, new_stream.native_handle(), gpu.native_handle(), f, args...);

      // return a new event
      return event(std::move(new_stream));
    }

  private:
    stream stream_;
    cudaEvent_t e_;

    // this function returns 0 so that we can pass it as an argument to swallow(...)
    __host__ __device__
    int destroy_event()
    {
#if __cuda_lib_has_cudart
      // since this will likely be called from destructors, swallow errors
      cudaError_t error = cudaEventDestroy(e_);
      e_ = 0;

#if __cuda_lib_has_printf
      if(error)
      {
        printf("CUDA error after cudaEventDestroy in cuda::detail::event::destroy_event: %s", cudaGetErrorString(error));
      } // end if
#endif // __cuda_lib_has_printf
#endif // __cuda_lib_has_cudart

      return 0;
    }

    __host__ __device__
    stream& stream()
    {
      return stream_;
    }

    // makes the given stream wait on the given event
    // XXX this should be moved into the implementation of detail::stream
    __host__ __device__
    static void stream_wait(const detail::stream& s, const detail::event& e)
    {
#if __cuda_lib_has_cudart
      // make the next launch wait on the event
      throw_on_error(cudaStreamWaitEvent(s.native_handle(), e.e_, 0), "cuda::detail::event::stream_wait(): cudaStreamWaitEvent()");
#else
      throw_on_error(cudaErrorNotSupported, "cuda::detail::event::stream_wait(): cudaStreamWaitEvent() requires CUDART");
#endif // __cuda_lib_has_cudart
    }

    // makes the given stream wait on this event
    __host__ __device__
    int stream_wait(const detail::stream& s) const
    {
      stream_wait(s, *this);

      return 0;
    }

    // makes the given stream wait on this event and invalidates this event
    // this function returns 0 so that we can pass it as an argument to swallow(...)
    __host__ __device__
    int stream_wait_and_invalidate(const detail::stream& s)
    {
      stream_wait(s);

      // this operation invalidates this event
      destroy_event();

      return 0;
    }

    // makes this event's stream wait on this event and invalidates this event
    __host__ __device__
    int stream_wait_and_invalidate()
    {
      return stream_wait_and_invalidate(stream());
    }

    template<class... Args>
    inline __host__ __device__
    static void swallow(Args&&... args) {}

    template<class... Events>
    __host__ __device__
    friend event when_all_events_are_ready(const gpu_id& gpu, Events&... events);

    template<class... Events>
    __host__ __device__
    friend event when_all_events_are_ready(Events&... events);
};

template<class... Events>
__host__ __device__
event when_all_events_are_ready(const gpu_id& gpu, Events&... events)
{
  detail::stream s{gpu};

  // tell the stream to wait on all the events
  event::swallow(events.stream_wait_and_invalidate(s)...);

  // return a new event recorded on the stream
  return event(std::move(s));
}

template<class... Events>
__host__ __device__
event when_all_events_are_ready(Events&... events)
{
  // just use the current gpu
  // XXX we might prefer the gpu associated with the first event
  return agency::cuda::detail::when_all_events_are_ready(current_gpu(), events...);
}

} // end detail
} // end cuda
} // end agency

<commit_msg>Correct the implementation of event's move constructor to avoid leaking cudaEvent_ts<commit_after>#pragma once

#include <agency/detail/config.hpp>
#include <agency/cuda/detail/feature_test.hpp>
#include <agency/cuda/detail/terminate.hpp>
#include <agency/cuda/detail/launch_kernel.hpp>
#include <agency/cuda/detail/kernel.hpp>
#include <agency/cuda/detail/future/stream.hpp>
#include <agency/cuda/gpu.hpp>

namespace agency
{
namespace cuda
{
namespace detail
{

class event
{
  public:
    struct construct_ready_t {};
    static constexpr construct_ready_t construct_ready{};

  private:
    static constexpr int event_create_flags = cudaEventDisableTiming;

    __host__ __device__
    event(construct_ready_t, detail::stream&& s)
      : stream_(std::move(s))
    {
#if __cuda_lib_has_cudart
      detail::throw_on_error(cudaEventCreateWithFlags(&e_, event_create_flags), "cudaEventCreateWithFlags in cuda::detail::event ctor");
#else
      detail::terminate_with_message("cuda::detail::event ctor requires CUDART");
#endif
    }

    // constructs a new event recorded on the given stream
    __host__ __device__
    event(detail::stream&& s) : event(construct_ready, std::move(s))
    {
#if __cuda_lib_has_cudart
      detail::throw_on_error(cudaEventRecord(e_, stream().native_handle()), "cudaEventRecord in cuda::detail::event ctor");
#else
      detail::terminate_with_message("cuda::detail::event ctor requires CUDART");
#endif
    }

  public:
    // creates an invalid event
    __host__ __device__
    event() : stream_(), e_{0}
    {}

    __host__ __device__
    event(construct_ready_t)
      : event(construct_ready, detail::stream())
    {}

    __host__ __device__
    event(const event&) = delete;

    __host__ __device__
    event(event&& other)
      : event()
    {
      swap(other);
    }

    __host__ __device__
    ~event()
    {
      if(valid())
      {
        destroy_event();
      }
    }

    __host__ __device__
    bool valid() const
    {
      return e_ != 0;
    }

    __host__ __device__
    bool is_ready() const
    {
      if(valid())
      {
#if __cuda_lib_has_cudart
        cudaError_t result = cudaEventQuery(e_);

        if(result != cudaErrorNotReady && result != cudaSuccess)
        {
          detail::throw_on_error(result, "cudaEventQuery in cuda::detail::event::is_ready");
        }

        return result == cudaSuccess;
#else
        detail::terminate_with_message("cuda::detail::event::is_ready requires CUDART");
#endif
      }

      return false;
    }

    __host__ __device__
    void wait() const
    {
      // XXX should probably check for valid() here

#if __cuda_lib_has_cudart

#ifndef __CUDA_ARCH__
      detail::throw_on_error(cudaEventSynchronize(e_), "cudaEventSynchronize in cuda::detail::event::wait");
#else
      detail::throw_on_error(cudaDeviceSynchronize(), "cudaDeviceSynchronize in cuda::detail::event::wait");
#endif // __CUDA_ARCH__

#else
      detail::terminate_with_message("cuda::detail::event::wait requires CUDART");
#endif // __cuda_lib_has_cudart
    }

    __host__ __device__
    void swap(event& other)
    {
      stream().swap(other.stream());

      cudaEvent_t tmp = e_;
      e_ = other.e_;
      other.e_ = tmp;
    }

    template<class Function, class... Args>
    __host__ __device__
    static void *then_kernel()
    {
      return reinterpret_cast<void*>(&cuda_kernel<Function,Args...>);
    }

    template<class Function, class... Args>
    __host__ __device__
    static void *then_kernel(const Function&, const Args&...)
    {
      return then_kernel<Function,Args...>();
    }

    // this form of then() leaves this event in a valid state afterwards
    template<class Function, class... Args>
    __host__ __device__
    event then(Function f, dim3 grid_dim, dim3 block_dim, int shared_memory_size, const Args&... args)
    {
      // create a stream for the kernel
      detail::stream result_stream;

      // make the result_stream wait on this event before further launches
      stream_wait(result_stream, *this);

      // get the address of the kernel
      auto kernel = then_kernel(f,args...);

      // launch the kernel on the result's stream
      detail::checked_launch_kernel(kernel, grid_dim, block_dim, shared_memory_size, result_stream.native_handle(), f, args...);

      // return a new event
      return event(std::move(result_stream));
    }

    // this form of then() leaves this event in an invalid state afterwards
    template<class Function, class... Args>
    __host__ __device__
    event then_and_invalidate(Function f, dim3 grid_dim, dim3 block_dim, int shared_memory_size, const Args&... args)
    {
      // make the stream wait on this event before further launches
      stream_wait_and_invalidate();

      // get the address of the kernel
      auto kernel = then_kernel(f,args...);

      // launch the kernel on this event's stream
      detail::checked_launch_kernel(kernel, grid_dim, block_dim, shared_memory_size, stream().native_handle(), f, args...);

      // return a new event
      return event(std::move(stream()));
    }

    template<class Function, class... Args>
    __host__ __device__
    static void *then_on_kernel()
    {
      return reinterpret_cast<void*>(&cuda_kernel<Function,Args...>);
    }

    template<class Function, class... Args>
    __host__ __device__
    static void *then_on_kernel(const Function&, const Args&...)
    {
      return then_on_kernel<Function,Args...>();
    }

    // this form of then_on() leaves this event in an invalid state afterwards
    template<class Function, class... Args>
    __host__ __device__
    event then_on_and_invalidate(Function f, dim3 grid_dim, dim3 block_dim, int shared_memory_size, const gpu_id& gpu, const Args&... args)
    {
      // make the stream wait on this event before further launches
      stream_wait_and_invalidate();

      // get the address of the kernel
      auto kernel = then_on_kernel(f,args...);

      // if gpu differs from this event's stream's gpu, we need to create a new one for the launch
      // otherwise, just reuse this event's stream
      detail::stream new_stream = (gpu == stream().gpu()) ? std::move(stream()) : detail::stream(gpu);

      // launch the kernel on the new stream
      detail::checked_launch_kernel_on_device(kernel, grid_dim, block_dim, shared_memory_size, new_stream.native_handle(), gpu.native_handle(), f, args...);

      // return a new event
      return event(std::move(new_stream));
    }

  private:
    stream stream_;
    cudaEvent_t e_;

    // this function returns 0 so that we can pass it as an argument to swallow(...)
    __host__ __device__
    int destroy_event()
    {
#if __cuda_lib_has_cudart
      // since this will likely be called from destructors, swallow errors
      cudaError_t error = cudaEventDestroy(e_);
      e_ = 0;

#if __cuda_lib_has_printf
      if(error)
      {
        printf("CUDA error after cudaEventDestroy in cuda::detail::event::destroy_event: %s", cudaGetErrorString(error));
      } // end if
#endif // __cuda_lib_has_printf
#endif // __cuda_lib_has_cudart

      return 0;
    }

    __host__ __device__
    stream& stream()
    {
      return stream_;
    }

    // makes the given stream wait on the given event
    // XXX this should be moved into the implementation of detail::stream
    __host__ __device__
    static void stream_wait(const detail::stream& s, const detail::event& e)
    {
#if __cuda_lib_has_cudart
      // make the next launch wait on the event
      throw_on_error(cudaStreamWaitEvent(s.native_handle(), e.e_, 0), "cuda::detail::event::stream_wait(): cudaStreamWaitEvent()");
#else
      throw_on_error(cudaErrorNotSupported, "cuda::detail::event::stream_wait(): cudaStreamWaitEvent() requires CUDART");
#endif // __cuda_lib_has_cudart
    }

    // makes the given stream wait on this event
    __host__ __device__
    int stream_wait(const detail::stream& s) const
    {
      stream_wait(s, *this);

      return 0;
    }

    // makes the given stream wait on this event and invalidates this event
    // this function returns 0 so that we can pass it as an argument to swallow(...)
    __host__ __device__
    int stream_wait_and_invalidate(const detail::stream& s)
    {
      stream_wait(s);

      // this operation invalidates this event
      destroy_event();

      return 0;
    }

    // makes this event's stream wait on this event and invalidates this event
    __host__ __device__
    int stream_wait_and_invalidate()
    {
      return stream_wait_and_invalidate(stream());
    }

    template<class... Args>
    inline __host__ __device__
    static void swallow(Args&&... args) {}

    template<class... Events>
    __host__ __device__
    friend event when_all_events_are_ready(const gpu_id& gpu, Events&... events);

    template<class... Events>
    __host__ __device__
    friend event when_all_events_are_ready(Events&... events);
};

template<class... Events>
__host__ __device__
event when_all_events_are_ready(const gpu_id& gpu, Events&... events)
{
  detail::stream s{gpu};

  // tell the stream to wait on all the events
  event::swallow(events.stream_wait_and_invalidate(s)...);

  // return a new event recorded on the stream
  return event(std::move(s));
}

template<class... Events>
__host__ __device__
event when_all_events_are_ready(Events&... events)
{
  // just use the current gpu
  // XXX we might prefer the gpu associated with the first event
  return agency::cuda::detail::when_all_events_are_ready(current_gpu(), events...);
}

} // end detail
} // end cuda
} // end agency

<|endoftext|>