<commit_before>/**
 * OpenAL cross platform audio library
 * Copyright (C) 2018 by Raul Herraiz.
 * This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Library General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 *  License along with this library; if not, write to the
 *  Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 * Or go to http://www.gnu.org/copyleft/lgpl.html
 */

#include "config.h"

#include <cmath>
#include <cstdlib>
#include <array>
#include <complex>
#include <algorithm>

#include "al/auxeffectslot.h"
#include "alcmain.h"
#include "alcontext.h"
#include "alu.h"

#include "alcomplex.h"

namespace {

using complex_d = std::complex<double>;

#define HIL_SIZE 1024
#define OVERSAMP (1<<2)

#define HIL_STEP     (HIL_SIZE / OVERSAMP)
#define FIFO_LATENCY (HIL_STEP * (OVERSAMP-1))

/* Define a Hann window, used to filter the HIL input and output. */
std::array<double,HIL_SIZE> InitHannWindow()
{
    std::array<double,HIL_SIZE> ret;
    /* Create lookup table of the Hann window for the desired size, i.e. HIL_SIZE */
    for(size_t i{0};i < HIL_SIZE>>1;i++)
    {
        constexpr double scale{al::MathDefs<double>::Pi() / double{HIL_SIZE-1}};
        const double val{std::sin(static_cast<double>(i) * scale)};
        ret[i] = ret[HIL_SIZE-1-i] = val * val;
    }
    return ret;
}
alignas(16) const std::array<double,HIL_SIZE> HannWindow = InitHannWindow();

struct FshifterState final : public EffectState {
    /* Effect parameters */
    size_t   mCount{};
    ALsizei  mPhaseStep[2]{};
    ALsizei  mPhase[2]{};
    ALdouble mSign[2]{};

    /*Effects buffers*/
    ALfloat   mInFIFO[HIL_SIZE]{};
    complex_d mOutFIFO[HIL_SIZE]{};
    complex_d mOutputAccum[HIL_SIZE]{};
    complex_d mAnalytic[HIL_SIZE]{};
    complex_d mOutdata[BUFFERSIZE]{};

    alignas(16) ALfloat mBufferOut[BUFFERSIZE]{};

    /* Effect gains for each output channel */
    struct {
        ALfloat Current[MAX_OUTPUT_CHANNELS]{};
        ALfloat Target[MAX_OUTPUT_CHANNELS]{};
    } mGains[2];

    ALboolean deviceUpdate(const ALCdevice *device) override;
    void update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target) override;
    void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn, const al::span<FloatBufferLine> samplesOut) override;

    DEF_NEWDEL(FshifterState)
};

ALboolean FshifterState::deviceUpdate(const ALCdevice*)
{
    /* (Re-)initializing parameters and clear the buffers. */
    mCount = FIFO_LATENCY;

    std::fill(std::begin(mPhaseStep),   std::end(mPhaseStep),   0);
    std::fill(std::begin(mPhase),       std::end(mPhase),       0);
    std::fill(std::begin(mSign),        std::end(mSign),        1.0);
    std::fill(std::begin(mInFIFO),      std::end(mInFIFO),      0.0f);
    std::fill(std::begin(mOutFIFO),     std::end(mOutFIFO),     complex_d{});
    std::fill(std::begin(mOutputAccum), std::end(mOutputAccum), complex_d{});
    std::fill(std::begin(mAnalytic),    std::end(mAnalytic),    complex_d{});

    for(auto &gain : mGains)
    {
        std::fill(std::begin(gain.Current), std::end(gain.Current), 0.0f);
        std::fill(std::begin(gain.Target), std::end(gain.Target), 0.0f);
    }

    return AL_TRUE;
}

void FshifterState::update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target)
{
    const ALCdevice *device{context->mDevice.get()};

    ALfloat step{props->Fshifter.Frequency / static_cast<ALfloat>(device->Frequency)};
    mPhaseStep[0] = mPhaseStep[1] = fastf2i(minf(step, 0.5f) * FRACTIONONE);

    switch(props->Fshifter.LeftDirection)
    {
    case AL_FREQUENCY_SHIFTER_DIRECTION_DOWN:
        mSign[0] = -1.0;
        break;

    case AL_FREQUENCY_SHIFTER_DIRECTION_UP:
        mSign[0] = 1.0;
        break;

    case AL_FREQUENCY_SHIFTER_DIRECTION_OFF:
        mPhase[0]     = 0;
        mPhaseStep[0] = 0;
        break;
    }

    switch (props->Fshifter.RightDirection)
    {
    case AL_FREQUENCY_SHIFTER_DIRECTION_DOWN:
        mSign[1] = -1.0;
        break;

    case AL_FREQUENCY_SHIFTER_DIRECTION_UP:
        mSign[1] = 1.0;
        break;

    case AL_FREQUENCY_SHIFTER_DIRECTION_OFF:
        mPhase[1]     = 0;
        mPhaseStep[1] = 0;
        break;
    }

    ALfloat coeffs[2][MAX_AMBI_CHANNELS];
    CalcDirectionCoeffs({-1.0f, 0.0f, -1.0f}, 0.0f, coeffs[0]);
    CalcDirectionCoeffs({ 1.0f, 0.0f, -1.0f}, 0.0f, coeffs[1]);

    mOutTarget = target.Main->Buffer;
    ComputePanGains(target.Main, coeffs[0], slot->Params.Gain, mGains[0].Target);
    ComputePanGains(target.Main, coeffs[1], slot->Params.Gain, mGains[1].Target);
}

void FshifterState::process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn, const al::span<FloatBufferLine> samplesOut)
{
    static constexpr complex_d complex_zero{0.0, 0.0};
    ALfloat *RESTRICT BufferOut = mBufferOut;
    size_t j, k;

    for(size_t base{0u};base < samplesToDo;)
    {
        const size_t todo{minz(HIL_SIZE-mCount, samplesToDo-base)};

        ASSUME(todo > 0);

        /* Fill FIFO buffer with samples data */
        k = mCount;
        for(j = 0;j < todo;j++,k++)
        {
            mInFIFO[k] = samplesIn[0][base+j];
            mOutdata[base+j] = mOutFIFO[k-FIFO_LATENCY];
        }
        mCount += todo;
        base += todo;

        /* Check whether FIFO buffer is filled */
        if(mCount < HIL_SIZE) continue;
        mCount = FIFO_LATENCY;

        /* Real signal windowing and store in Analytic buffer */
        for(k = 0;k < HIL_SIZE;k++)
        {
            mAnalytic[k].real(mInFIFO[k] * HannWindow[k]);
            mAnalytic[k].imag(0.0);
        }

        /* Processing signal by Discrete Hilbert Transform (analytical signal). */
        complex_hilbert(mAnalytic);

        /* Windowing and add to output accumulator */
        for(k = 0;k < HIL_SIZE;k++)
            mOutputAccum[k] += 2.0/OVERSAMP*HannWindow[k]*mAnalytic[k];

        /* Shift accumulator, input & output FIFO */
        for(k = 0;k < HIL_STEP;k++) mOutFIFO[k] = mOutputAccum[k];
        for(j = 0;k < HIL_SIZE;k++,j++) mOutputAccum[j] = mOutputAccum[k];
        for(;j < HIL_SIZE;j++) mOutputAccum[j] = complex_zero;
        for(k = 0;k < FIFO_LATENCY;k++)
            mInFIFO[k] = mInFIFO[k+HIL_STEP];
    }

    /* Process frequency shifter using the analytic signal obtained. */
    for(ALsizei c{0};c < 2;++c)
    {
        for(k = 0;k < samplesToDo;++k)
        {
            double phase = mPhase[c] * ((1.0 / FRACTIONONE) * al::MathDefs<double>::Tau());
            BufferOut[k] = static_cast<float>(mOutdata[k].real()*std::cos(phase) +
                mOutdata[k].imag()*std::sin(phase)*mSign[c]);

            mPhase[c] += mPhaseStep[c];
            mPhase[c] &= FRACTIONMASK;
        }

        /* Now, mix the processed sound data to the output. */
        MixSamples({BufferOut, samplesToDo}, samplesOut, mGains[c].Current, mGains[c].Target,
            maxz(samplesToDo, 512), 0);
    }
}

void Fshifter_setParamf(EffectProps *props, ALCcontext *context, ALenum param, ALfloat val)
{
    switch(param)
    {
        case AL_FREQUENCY_SHIFTER_FREQUENCY:
            if(!(val >= AL_FREQUENCY_SHIFTER_MIN_FREQUENCY && val <= AL_FREQUENCY_SHIFTER_MAX_FREQUENCY))
                SETERR_RETURN(context, AL_INVALID_VALUE,,"Frequency shifter frequency out of range");
            props->Fshifter.Frequency = val;
            break;

        default:
            context->setError(AL_INVALID_ENUM, "Invalid frequency shifter float property 0x%04x",
                param);
    }
}
void Fshifter_setParamfv(EffectProps *props, ALCcontext *context, ALenum param, const ALfloat *vals)
{ Fshifter_setParamf(props, context, param, vals[0]); }

void Fshifter_setParami(EffectProps *props, ALCcontext *context, ALenum param, ALint val)
{
    switch(param)
    {
        case AL_FREQUENCY_SHIFTER_LEFT_DIRECTION:
            if(!(val >= AL_FREQUENCY_SHIFTER_MIN_LEFT_DIRECTION && val <= AL_FREQUENCY_SHIFTER_MAX_LEFT_DIRECTION))
                SETERR_RETURN(context, AL_INVALID_VALUE,,"Frequency shifter left direction out of range");
            props->Fshifter.LeftDirection = val;
            break;

        case AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION:
            if(!(val >= AL_FREQUENCY_SHIFTER_MIN_RIGHT_DIRECTION && val <= AL_FREQUENCY_SHIFTER_MAX_RIGHT_DIRECTION))
                SETERR_RETURN(context, AL_INVALID_VALUE,,"Frequency shifter right direction out of range");
            props->Fshifter.RightDirection = val;
            break;

        default:
            context->setError(AL_INVALID_ENUM, "Invalid frequency shifter integer property 0x%04x",
                param);
    }
}
void Fshifter_setParamiv(EffectProps *props, ALCcontext *context, ALenum param, const ALint *vals)
{ Fshifter_setParami(props, context, param, vals[0]); }

void Fshifter_getParami(const EffectProps *props, ALCcontext *context, ALenum param, ALint *val)
{
    switch(param)
    {
        case AL_FREQUENCY_SHIFTER_LEFT_DIRECTION:
            *val = props->Fshifter.LeftDirection;
            break;
        case AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION:
            *val = props->Fshifter.RightDirection;
            break;
        default:
            context->setError(AL_INVALID_ENUM, "Invalid frequency shifter integer property 0x%04x",
                param);
    }
}
void Fshifter_getParamiv(const EffectProps *props, ALCcontext *context, ALenum param, ALint *vals)
{ Fshifter_getParami(props, context, param, vals); }

void Fshifter_getParamf(const EffectProps *props, ALCcontext *context, ALenum param, ALfloat *val)
{
    switch(param)
    {
        case AL_FREQUENCY_SHIFTER_FREQUENCY:
            *val = props->Fshifter.Frequency;
            break;

        default:
            context->setError(AL_INVALID_ENUM, "Invalid frequency shifter float property 0x%04x",
                param);
    }
}
void Fshifter_getParamfv(const EffectProps *props, ALCcontext *context, ALenum param, ALfloat *vals)
{ Fshifter_getParamf(props, context, param, vals); }

DEFINE_ALEFFECT_VTABLE(Fshifter);

struct FshifterStateFactory final : public EffectStateFactory {
    EffectState *create() override { return new FshifterState{}; }
    EffectProps getDefaultProps() const noexcept override;
    const EffectVtable *getEffectVtable() const noexcept override { return &Fshifter_vtable; }
};

EffectProps FshifterStateFactory::getDefaultProps() const noexcept
{
    EffectProps props{};
    props.Fshifter.Frequency      = AL_FREQUENCY_SHIFTER_DEFAULT_FREQUENCY;
    props.Fshifter.LeftDirection  = AL_FREQUENCY_SHIFTER_DEFAULT_LEFT_DIRECTION;
    props.Fshifter.RightDirection = AL_FREQUENCY_SHIFTER_DEFAULT_RIGHT_DIRECTION;
    return props;
}

} // namespace

EffectStateFactory *FshifterStateFactory_getFactory()
{
    static FshifterStateFactory FshifterFactory{};
    return &FshifterFactory;
}
<commit_msg>Fix the frequency shifter channel panning<commit_after>/**
 * OpenAL cross platform audio library
 * Copyright (C) 2018 by Raul Herraiz.
 * This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Library General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 *  License along with this library; if not, write to the
 *  Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 * Or go to http://www.gnu.org/copyleft/lgpl.html
 */

#include "config.h"

#include <cmath>
#include <cstdlib>
#include <array>
#include <complex>
#include <algorithm>

#include "al/auxeffectslot.h"
#include "alcmain.h"
#include "alcontext.h"
#include "alu.h"

#include "alcomplex.h"

namespace {

using complex_d = std::complex<double>;

#define HIL_SIZE 1024
#define OVERSAMP (1<<2)

#define HIL_STEP     (HIL_SIZE / OVERSAMP)
#define FIFO_LATENCY (HIL_STEP * (OVERSAMP-1))

/* Define a Hann window, used to filter the HIL input and output. */
std::array<double,HIL_SIZE> InitHannWindow()
{
    std::array<double,HIL_SIZE> ret;
    /* Create lookup table of the Hann window for the desired size, i.e. HIL_SIZE */
    for(size_t i{0};i < HIL_SIZE>>1;i++)
    {
        constexpr double scale{al::MathDefs<double>::Pi() / double{HIL_SIZE-1}};
        const double val{std::sin(static_cast<double>(i) * scale)};
        ret[i] = ret[HIL_SIZE-1-i] = val * val;
    }
    return ret;
}
alignas(16) const std::array<double,HIL_SIZE> HannWindow = InitHannWindow();

struct FshifterState final : public EffectState {
    /* Effect parameters */
    size_t   mCount{};
    ALsizei  mPhaseStep[2]{};
    ALsizei  mPhase[2]{};
    ALdouble mSign[2]{};

    /*Effects buffers*/
    ALfloat   mInFIFO[HIL_SIZE]{};
    complex_d mOutFIFO[HIL_SIZE]{};
    complex_d mOutputAccum[HIL_SIZE]{};
    complex_d mAnalytic[HIL_SIZE]{};
    complex_d mOutdata[BUFFERSIZE]{};

    alignas(16) ALfloat mBufferOut[BUFFERSIZE]{};

    /* Effect gains for each output channel */
    struct {
        ALfloat Current[MAX_OUTPUT_CHANNELS]{};
        ALfloat Target[MAX_OUTPUT_CHANNELS]{};
    } mGains[2];

    ALboolean deviceUpdate(const ALCdevice *device) override;
    void update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target) override;
    void process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn, const al::span<FloatBufferLine> samplesOut) override;

    DEF_NEWDEL(FshifterState)
};

ALboolean FshifterState::deviceUpdate(const ALCdevice*)
{
    /* (Re-)initializing parameters and clear the buffers. */
    mCount = FIFO_LATENCY;

    std::fill(std::begin(mPhaseStep),   std::end(mPhaseStep),   0);
    std::fill(std::begin(mPhase),       std::end(mPhase),       0);
    std::fill(std::begin(mSign),        std::end(mSign),        1.0);
    std::fill(std::begin(mInFIFO),      std::end(mInFIFO),      0.0f);
    std::fill(std::begin(mOutFIFO),     std::end(mOutFIFO),     complex_d{});
    std::fill(std::begin(mOutputAccum), std::end(mOutputAccum), complex_d{});
    std::fill(std::begin(mAnalytic),    std::end(mAnalytic),    complex_d{});

    for(auto &gain : mGains)
    {
        std::fill(std::begin(gain.Current), std::end(gain.Current), 0.0f);
        std::fill(std::begin(gain.Target), std::end(gain.Target), 0.0f);
    }

    return AL_TRUE;
}

void FshifterState::update(const ALCcontext *context, const ALeffectslot *slot, const EffectProps *props, const EffectTarget target)
{
    const ALCdevice *device{context->mDevice.get()};

    ALfloat step{props->Fshifter.Frequency / static_cast<ALfloat>(device->Frequency)};
    mPhaseStep[0] = mPhaseStep[1] = fastf2i(minf(step, 0.5f) * FRACTIONONE);

    switch(props->Fshifter.LeftDirection)
    {
    case AL_FREQUENCY_SHIFTER_DIRECTION_DOWN:
        mSign[0] = -1.0;
        break;

    case AL_FREQUENCY_SHIFTER_DIRECTION_UP:
        mSign[0] = 1.0;
        break;

    case AL_FREQUENCY_SHIFTER_DIRECTION_OFF:
        mPhase[0]     = 0;
        mPhaseStep[0] = 0;
        break;
    }

    switch (props->Fshifter.RightDirection)
    {
    case AL_FREQUENCY_SHIFTER_DIRECTION_DOWN:
        mSign[1] = -1.0;
        break;

    case AL_FREQUENCY_SHIFTER_DIRECTION_UP:
        mSign[1] = 1.0;
        break;

    case AL_FREQUENCY_SHIFTER_DIRECTION_OFF:
        mPhase[1]     = 0;
        mPhaseStep[1] = 0;
        break;
    }

    ALfloat coeffs[2][MAX_AMBI_CHANNELS];
    CalcDirectionCoeffs({-1.0f, 0.0f, 0.0f}, 0.0f, coeffs[0]);
    CalcDirectionCoeffs({ 1.0f, 0.0f, 0.0f}, 0.0f, coeffs[1]);

    mOutTarget = target.Main->Buffer;
    ComputePanGains(target.Main, coeffs[0], slot->Params.Gain, mGains[0].Target);
    ComputePanGains(target.Main, coeffs[1], slot->Params.Gain, mGains[1].Target);
}

void FshifterState::process(const size_t samplesToDo, const al::span<const FloatBufferLine> samplesIn, const al::span<FloatBufferLine> samplesOut)
{
    static constexpr complex_d complex_zero{0.0, 0.0};
    ALfloat *RESTRICT BufferOut = mBufferOut;
    size_t j, k;

    for(size_t base{0u};base < samplesToDo;)
    {
        const size_t todo{minz(HIL_SIZE-mCount, samplesToDo-base)};

        ASSUME(todo > 0);

        /* Fill FIFO buffer with samples data */
        k = mCount;
        for(j = 0;j < todo;j++,k++)
        {
            mInFIFO[k] = samplesIn[0][base+j];
            mOutdata[base+j] = mOutFIFO[k-FIFO_LATENCY];
        }
        mCount += todo;
        base += todo;

        /* Check whether FIFO buffer is filled */
        if(mCount < HIL_SIZE) continue;
        mCount = FIFO_LATENCY;

        /* Real signal windowing and store in Analytic buffer */
        for(k = 0;k < HIL_SIZE;k++)
        {
            mAnalytic[k].real(mInFIFO[k] * HannWindow[k]);
            mAnalytic[k].imag(0.0);
        }

        /* Processing signal by Discrete Hilbert Transform (analytical signal). */
        complex_hilbert(mAnalytic);

        /* Windowing and add to output accumulator */
        for(k = 0;k < HIL_SIZE;k++)
            mOutputAccum[k] += 2.0/OVERSAMP*HannWindow[k]*mAnalytic[k];

        /* Shift accumulator, input & output FIFO */
        for(k = 0;k < HIL_STEP;k++) mOutFIFO[k] = mOutputAccum[k];
        for(j = 0;k < HIL_SIZE;k++,j++) mOutputAccum[j] = mOutputAccum[k];
        for(;j < HIL_SIZE;j++) mOutputAccum[j] = complex_zero;
        for(k = 0;k < FIFO_LATENCY;k++)
            mInFIFO[k] = mInFIFO[k+HIL_STEP];
    }

    /* Process frequency shifter using the analytic signal obtained. */
    for(ALsizei c{0};c < 2;++c)
    {
        for(k = 0;k < samplesToDo;++k)
        {
            double phase = mPhase[c] * ((1.0 / FRACTIONONE) * al::MathDefs<double>::Tau());
            BufferOut[k] = static_cast<float>(mOutdata[k].real()*std::cos(phase) +
                mOutdata[k].imag()*std::sin(phase)*mSign[c]);

            mPhase[c] += mPhaseStep[c];
            mPhase[c] &= FRACTIONMASK;
        }

        /* Now, mix the processed sound data to the output. */
        MixSamples({BufferOut, samplesToDo}, samplesOut, mGains[c].Current, mGains[c].Target,
            maxz(samplesToDo, 512), 0);
    }
}

void Fshifter_setParamf(EffectProps *props, ALCcontext *context, ALenum param, ALfloat val)
{
    switch(param)
    {
        case AL_FREQUENCY_SHIFTER_FREQUENCY:
            if(!(val >= AL_FREQUENCY_SHIFTER_MIN_FREQUENCY && val <= AL_FREQUENCY_SHIFTER_MAX_FREQUENCY))
                SETERR_RETURN(context, AL_INVALID_VALUE,,"Frequency shifter frequency out of range");
            props->Fshifter.Frequency = val;
            break;

        default:
            context->setError(AL_INVALID_ENUM, "Invalid frequency shifter float property 0x%04x",
                param);
    }
}
void Fshifter_setParamfv(EffectProps *props, ALCcontext *context, ALenum param, const ALfloat *vals)
{ Fshifter_setParamf(props, context, param, vals[0]); }

void Fshifter_setParami(EffectProps *props, ALCcontext *context, ALenum param, ALint val)
{
    switch(param)
    {
        case AL_FREQUENCY_SHIFTER_LEFT_DIRECTION:
            if(!(val >= AL_FREQUENCY_SHIFTER_MIN_LEFT_DIRECTION && val <= AL_FREQUENCY_SHIFTER_MAX_LEFT_DIRECTION))
                SETERR_RETURN(context, AL_INVALID_VALUE,,"Frequency shifter left direction out of range");
            props->Fshifter.LeftDirection = val;
            break;

        case AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION:
            if(!(val >= AL_FREQUENCY_SHIFTER_MIN_RIGHT_DIRECTION && val <= AL_FREQUENCY_SHIFTER_MAX_RIGHT_DIRECTION))
                SETERR_RETURN(context, AL_INVALID_VALUE,,"Frequency shifter right direction out of range");
            props->Fshifter.RightDirection = val;
            break;

        default:
            context->setError(AL_INVALID_ENUM, "Invalid frequency shifter integer property 0x%04x",
                param);
    }
}
void Fshifter_setParamiv(EffectProps *props, ALCcontext *context, ALenum param, const ALint *vals)
{ Fshifter_setParami(props, context, param, vals[0]); }

void Fshifter_getParami(const EffectProps *props, ALCcontext *context, ALenum param, ALint *val)
{
    switch(param)
    {
        case AL_FREQUENCY_SHIFTER_LEFT_DIRECTION:
            *val = props->Fshifter.LeftDirection;
            break;
        case AL_FREQUENCY_SHIFTER_RIGHT_DIRECTION:
            *val = props->Fshifter.RightDirection;
            break;
        default:
            context->setError(AL_INVALID_ENUM, "Invalid frequency shifter integer property 0x%04x",
                param);
    }
}
void Fshifter_getParamiv(const EffectProps *props, ALCcontext *context, ALenum param, ALint *vals)
{ Fshifter_getParami(props, context, param, vals); }

void Fshifter_getParamf(const EffectProps *props, ALCcontext *context, ALenum param, ALfloat *val)
{
    switch(param)
    {
        case AL_FREQUENCY_SHIFTER_FREQUENCY:
            *val = props->Fshifter.Frequency;
            break;

        default:
            context->setError(AL_INVALID_ENUM, "Invalid frequency shifter float property 0x%04x",
                param);
    }
}
void Fshifter_getParamfv(const EffectProps *props, ALCcontext *context, ALenum param, ALfloat *vals)
{ Fshifter_getParamf(props, context, param, vals); }

DEFINE_ALEFFECT_VTABLE(Fshifter);

struct FshifterStateFactory final : public EffectStateFactory {
    EffectState *create() override { return new FshifterState{}; }
    EffectProps getDefaultProps() const noexcept override;
    const EffectVtable *getEffectVtable() const noexcept override { return &Fshifter_vtable; }
};

EffectProps FshifterStateFactory::getDefaultProps() const noexcept
{
    EffectProps props{};
    props.Fshifter.Frequency      = AL_FREQUENCY_SHIFTER_DEFAULT_FREQUENCY;
    props.Fshifter.LeftDirection  = AL_FREQUENCY_SHIFTER_DEFAULT_LEFT_DIRECTION;
    props.Fshifter.RightDirection = AL_FREQUENCY_SHIFTER_DEFAULT_RIGHT_DIRECTION;
    return props;
}

} // namespace

EffectStateFactory *FshifterStateFactory_getFactory()
{
    static FshifterStateFactory FshifterFactory{};
    return &FshifterFactory;
}
<|endoftext|>


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

/*!
  \file cnf_view.hpp
  \brief Creates a CNF while creating a network

  \author Mathias Soeken
*/

#pragma once

#include <cstdint>
#include <vector>

#include "../algorithms/cnf.hpp"
#include "../traits.hpp"

#include <fmt/format.h>
#include <percy/solvers/bsat2.hpp>

namespace mockturtle
{

/*! \brief A view to connect logic network creation to SAT solving.
 *
 * When using this view to create a new network, it creates a CNF internally
 * while nodes are added to the network.  It also contains a SAT solver.  The
 * network can be solved by calling the `solve` method, which by default assumes
 * that each output should compute `true` (an overload of the `solve` method can
 * override this default behaviour and apply custom assumptions).  Further,
 * the methods `value` and `pi_values` can be used to access model values in
 * case solving was satisfiable.  Finally, methods `var` and `lit` can be used
 * to access variable and literal information for nodes and signals,
 * respectively, in order to add custom clauses with the `add_clause` methods.
 *
 * The CNF is generated additively and cannot be modified after nodes have been
 * added.  Therefore, a network cannot modify or delete nodes when wrapped in a
 * `cnf_view`.
 */
template<typename Ntk>
class cnf_view : public Ntk
{
public:
  using storage = typename Ntk::storage;
  using node = typename Ntk::node;
  using signal = typename Ntk::signal;

public:
  // can only be constructed as empty network
  cnf_view() : Ntk()
  {
    static_assert( is_network_type_v<Ntk>, "Ntk is not a network type" );
    static_assert( has_node_to_index_v<Ntk>, "Ntk does not implement the node_to_index method" );
    static_assert( has_get_node_v<Ntk>, "Ntk does not implement the get_node method" );
    static_assert( has_make_signal_v<Ntk>, "Ntk does not implement the make_signal method" );
    static_assert( has_foreach_pi_v<Ntk>, "Ntk does not implement the foreach_pi method" );
    static_assert( has_foreach_po_v<Ntk>, "Ntk does not implement the foreach_po method" );
    static_assert( has_foreach_fanin_v<Ntk>, "Ntk does not implement the foreach_fanin method" );
    static_assert( has_node_function_v<Ntk>, "Ntk does not implement the node_function method" );

    register_events();
  }

  /* \brief Returns the variable associated to a node. */
  inline uint32_t var( node const& n ) const
  {
    return Ntk::node_to_index( n );
  }

  /*! \brief Returns the literal associated to a signal. */
  inline uint32_t lit( signal const& f ) const
  {
    return make_lit( var( Ntk::get_node( f ) ), Ntk::is_complemented( f ) );
  }

  /*! \brief Solves the network with a set of custom assumptions.
   *
   * This function does not assert any primary output, unless specified
   * explicitly through the assumptions.
   *
   * The function returns `nullopt`, if no solution can be found (due to a
   * conflict limit), or `true` in case of SAT, and `false` in case of UNSAT.
   *
   * \param assumptions Vector of literals to be assumped when solving
   * \param limit Conflict limit (unlimited if 0)
   */
  inline std::optional<bool> solve( std::vector<int> assumptions, int limit = 0 )
  {
    switch ( solver_.solve( &assumptions[0], &assumptions[0] + assumptions.size(), limit ) )
    {
    case percy::success:
      return true;
    case percy::failure:
      return false;
    default:
    case percy::timeout:
      return std::nullopt;
    }
  }

  /*! \brief Solves the network by asserting all primary outputs to be true
   *
   * The function returns `nullopt`, if no solution can be found (due to a
   * conflict limit), or `true` in case of SAT, and `false` in case of UNSAT.
   *
   * \param limit Conflict limit (unlimited if 0)
   */
  inline std::optional<bool> solve( int limit = 0 )
  {
    std::vector<int> assumptions;
    Ntk::foreach_po( [&]( auto const& f ) {
      assumptions.push_back( lit( f ) );
    } );
    return solve( assumptions, limit );
  }

  /*! \brief Return model value for a node. */
  inline bool value( node const& n )
  {
    return solver_.var_value( var( n ) );
  }

  /* \brief Returns all model values for all primary inputs. */
  std::vector<bool> pi_values()
  {
    std::vector<bool> values( Ntk::num_pis() );
    Ntk::foreach_pi( [&]( auto const& n, auto i ) {
      values[i] = value( n );
    } );
    return values;
  }

  /*! \brief Blocks last model for primary input values. */
  void block()
  {
    std::vector<uint32_t> blocking_clause( Ntk::num_pis() );
    Ntk::foreach_pi( [&]( auto const& n, auto i ) {
      blocking_clause[i] = make_lit( var( n ), value( n ) );
    });
    add_clause( blocking_clause );
  }

  /*! \brief Number of variables. */
  inline uint32_t num_vars()
  {
    return solver_.nr_vars();
  }

  /*! \brief Number of clauses. */
  inline uint32_t num_clauses()
  {
    return solver_.nr_clauses();
  }

  /*! \brief Adds a clause to the solver. */
  void add_clause( std::vector<uint32_t> const& clause )
  {
    solver_.add_clause( clause );
  }

  /*! \brief Adds a clause to the solver.
   *
   * Entries are either all literals or network signals.
   */
  template<typename... Lit, typename = std::enable_if_t<
    std::disjunction_v<
      std::conjunction<std::is_same<Lit, uint32_t>...>,
      std::conjunction<std::is_same<Lit, signal>...>>>>
  void add_clause( Lit... lits )
  {
    if constexpr ( std::conjunction_v<std::is_same<Lit, uint32_t>...> )
    {
      solver_.add_clause( std::vector<uint32_t>{{lits...}} );
    }
    else
    {
      solver_.add_clause( std::vector<uint32_t>{{lit( lits )...}} );
    }
  }

private:
  void register_events()
  {
    Ntk::events().on_add.push_back( [this]( auto const& n ) { on_add( n ); } );
    Ntk::events().on_modified.push_back( []( auto const& n, auto const& previous ) {
      (void)n;
      (void)previous;
      assert( false && "nodes should not be modified in cnf_view" );
      std::abort();
    } );
    Ntk::events().on_delete.push_back( []( auto const& n ) {
      (void)n;
      assert( false && "nodes should not be deleted in cnf_view" );
      std::abort();
    } );
  }

  void on_add( node const& n )
  {
    const auto _add_clause = [&]( std::vector<uint32_t> const& clause ) {
      add_clause( clause );
    };

    const auto node_lit = lit( Ntk::make_signal( n ) );
    std::vector<uint32_t> child_lits;
    Ntk::foreach_fanin( n, [&]( auto const& f ) {
      child_lits.push_back( lit( f ) );
    } );

    if constexpr ( has_is_and_v<Ntk> )
    {
      if ( Ntk::is_and( n ) )
      {
        detail::on_and( node_lit, child_lits[0], child_lits[1], _add_clause );
        return;
      }
    }

    if constexpr ( has_is_or_v<Ntk> )
    {
      if ( Ntk::is_or( n ) )
      {
        detail::on_or( node_lit, child_lits[0], child_lits[1], _add_clause );
        return;
      }
    }

    if constexpr ( has_is_xor_v<Ntk> )
    {
      if ( Ntk::is_xor( n ) )
      {
        detail::on_xor( node_lit, child_lits[0], child_lits[1], _add_clause );
        return;
      }
    }

    if constexpr ( has_is_maj_v<Ntk> )
    {
      if ( Ntk::is_maj( n ) )
      {
        detail::on_maj( node_lit, child_lits[0], child_lits[1], child_lits[2], _add_clause );
        return;
      }
    }

    if constexpr ( has_is_ite_v<Ntk> )
    {
      if ( Ntk::is_ite( n ) )
      {
        detail::on_ite( node_lit, child_lits[0], child_lits[1], child_lits[2], _add_clause );
        return;
      }
    }

    if constexpr ( has_is_xor3_v<Ntk> )
    {
      if ( Ntk::is_xor3( n ) )
      {
        detail::on_xor3( node_lit, child_lits[0], child_lits[1], child_lits[2], _add_clause );
        return;
      }
    }

    detail::on_function( node_lit, child_lits, Ntk::node_function( n ), _add_clause );
  }

private:
  percy::bsat_wrapper solver_;
};

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

/*!
  \file cnf_view.hpp
  \brief Creates a CNF while creating a network

  \author Mathias Soeken
*/

#pragma once

#include <cstdint>
#include <vector>

#include "../algorithms/cnf.hpp"
#include "../traits.hpp"

#include <fmt/format.h>
#include <percy/solvers/bsat2.hpp>

namespace mockturtle
{

/*! \brief A view to connect logic network creation to SAT solving.
 *
 * When using this view to create a new network, it creates a CNF internally
 * while nodes are added to the network.  It also contains a SAT solver.  The
 * network can be solved by calling the `solve` method, which by default assumes
 * that each output should compute `true` (an overload of the `solve` method can
 * override this default behaviour and apply custom assumptions).  Further,
 * the methods `value` and `pi_values` can be used to access model values in
 * case solving was satisfiable.  Finally, methods `var` and `lit` can be used
 * to access variable and literal information for nodes and signals,
 * respectively, in order to add custom clauses with the `add_clause` methods.
 *
 * The CNF is generated additively and cannot be modified after nodes have been
 * added.  Therefore, a network cannot modify or delete nodes when wrapped in a
 * `cnf_view`.
 */
template<typename Ntk>
class cnf_view : public Ntk
{
public:
  using storage = typename Ntk::storage;
  using node = typename Ntk::node;
  using signal = typename Ntk::signal;

public:
  // can only be constructed as empty network
  cnf_view() : Ntk()
  {
    static_assert( is_network_type_v<Ntk>, "Ntk is not a network type" );
    static_assert( has_node_to_index_v<Ntk>, "Ntk does not implement the node_to_index method" );
    static_assert( has_get_node_v<Ntk>, "Ntk does not implement the get_node method" );
    static_assert( has_make_signal_v<Ntk>, "Ntk does not implement the make_signal method" );
    static_assert( has_foreach_pi_v<Ntk>, "Ntk does not implement the foreach_pi method" );
    static_assert( has_foreach_po_v<Ntk>, "Ntk does not implement the foreach_po method" );
    static_assert( has_foreach_fanin_v<Ntk>, "Ntk does not implement the foreach_fanin method" );
    static_assert( has_node_function_v<Ntk>, "Ntk does not implement the node_function method" );

    register_events();
  }

  /* \brief Returns the variable associated to a node. */
  inline uint32_t var( node const& n ) const
  {
    return Ntk::node_to_index( n );
  }

  /*! \brief Returns the literal associated to a signal. */
  inline uint32_t lit( signal const& f ) const
  {
    return make_lit( var( Ntk::get_node( f ) ), Ntk::is_complemented( f ) );
  }

  /*! \brief Solves the network with a set of custom assumptions.
   *
   * This function does not assert any primary output, unless specified
   * explicitly through the assumptions.
   *
   * The function returns `nullopt`, if no solution can be found (due to a
   * conflict limit), or `true` in case of SAT, and `false` in case of UNSAT.
   *
   * \param assumptions Vector of literals to be assumped when solving
   * \param limit Conflict limit (unlimited if 0)
   */
  inline std::optional<bool> solve( std::vector<int> assumptions, int limit = 0 )
  {
    switch ( solver_.solve( &assumptions[0], &assumptions[0] + assumptions.size(), limit ) )
    {
    case percy::success:
      return true;
    case percy::failure:
      return false;
    default:
    case percy::timeout:
      return std::nullopt;
    }
  }

  /*! \brief Solves the network by asserting all primary outputs to be true
   *
   * The function returns `nullopt`, if no solution can be found (due to a
   * conflict limit), or `true` in case of SAT, and `false` in case of UNSAT.
   *
   * \param limit Conflict limit (unlimited if 0)
   */
  inline std::optional<bool> solve( int limit = 0 )
  {
    std::vector<int> assumptions;
    Ntk::foreach_po( [&]( auto const& f ) {
      assumptions.push_back( lit( f ) );
    } );
    return solve( assumptions, limit );
  }

  /*! \brief Return model value for a node. */
  inline bool value( node const& n )
  {
    return solver_.var_value( var( n ) );
  }

  /* \brief Returns all model values for all primary inputs. */
  std::vector<bool> pi_values()
  {
    std::vector<bool> values( Ntk::num_pis() );
    Ntk::foreach_pi( [&]( auto const& n, auto i ) {
      values[i] = value( n );
    } );
    return values;
  }

  /*! \brief Blocks last model for primary input values. */
  void block()
  {
    std::vector<uint32_t> blocking_clause( Ntk::num_pis() );
    Ntk::foreach_pi( [&]( auto const& n, auto i ) {
      blocking_clause[i] = make_lit( var( n ), value( n ) );
    });
    add_clause( blocking_clause );
  }

  /*! \brief Number of variables. */
  inline uint32_t num_vars()
  {
    return solver_.nr_vars();
  }

  /*! \brief Number of clauses. */
  inline uint32_t num_clauses()
  {
    return solver_.nr_clauses();
  }

  /*! \brief Adds a clause to the solver. */
  void add_clause( std::vector<uint32_t> const& clause )
  {
    solver_.add_clause( clause );
  }

  /*! \brief Adds a clause from signals to the solver. */
  void add_clause( std::vector<signal> const& clause )
  {
    std::vector<uint32_t> lits( clause.size() );
    std::transform( clause.begin(), clause.end(), lits.begin(), [&]( auto const& s ) { return lit( s ); } );
    solver_.add_clause( lits );
  }

  /*! \brief Adds a clause to the solver.
   *
   * Entries are either all literals or network signals.
   */
  template<typename... Lit, typename = std::enable_if_t<
    std::disjunction_v<
      std::conjunction<std::is_same<Lit, uint32_t>...>,
      std::conjunction<std::is_same<Lit, signal>...>>>>
  void add_clause( Lit... lits )
  {
    if constexpr ( std::conjunction_v<std::is_same<Lit, uint32_t>...> )
    {
      solver_.add_clause( std::vector<uint32_t>{{lits...}} );
    }
    else
    {
      solver_.add_clause( std::vector<uint32_t>{{lit( lits )...}} );
    }
  }

private:
  void register_events()
  {
    Ntk::events().on_add.push_back( [this]( auto const& n ) { on_add( n ); } );
    Ntk::events().on_modified.push_back( []( auto const& n, auto const& previous ) {
      (void)n;
      (void)previous;
      assert( false && "nodes should not be modified in cnf_view" );
      std::abort();
    } );
    Ntk::events().on_delete.push_back( []( auto const& n ) {
      (void)n;
      assert( false && "nodes should not be deleted in cnf_view" );
      std::abort();
    } );
  }

  void on_add( node const& n )
  {
    const auto _add_clause = [&]( std::vector<uint32_t> const& clause ) {
      add_clause( clause );
    };

    const auto node_lit = lit( Ntk::make_signal( n ) );
    std::vector<uint32_t> child_lits;
    Ntk::foreach_fanin( n, [&]( auto const& f ) {
      child_lits.push_back( lit( f ) );
    } );

    if constexpr ( has_is_and_v<Ntk> )
    {
      if ( Ntk::is_and( n ) )
      {
        detail::on_and( node_lit, child_lits[0], child_lits[1], _add_clause );
        return;
      }
    }

    if constexpr ( has_is_or_v<Ntk> )
    {
      if ( Ntk::is_or( n ) )
      {
        detail::on_or( node_lit, child_lits[0], child_lits[1], _add_clause );
        return;
      }
    }

    if constexpr ( has_is_xor_v<Ntk> )
    {
      if ( Ntk::is_xor( n ) )
      {
        detail::on_xor( node_lit, child_lits[0], child_lits[1], _add_clause );
        return;
      }
    }

    if constexpr ( has_is_maj_v<Ntk> )
    {
      if ( Ntk::is_maj( n ) )
      {
        detail::on_maj( node_lit, child_lits[0], child_lits[1], child_lits[2], _add_clause );
        return;
      }
    }

    if constexpr ( has_is_ite_v<Ntk> )
    {
      if ( Ntk::is_ite( n ) )
      {
        detail::on_ite( node_lit, child_lits[0], child_lits[1], child_lits[2], _add_clause );
        return;
      }
    }

    if constexpr ( has_is_xor3_v<Ntk> )
    {
      if ( Ntk::is_xor3( n ) )
      {
        detail::on_xor3( node_lit, child_lits[0], child_lits[1], child_lits[2], _add_clause );
        return;
      }
    }

    detail::on_function( node_lit, child_lits, Ntk::node_function( n ), _add_clause );
  }

private:
  percy::bsat_wrapper solver_;
};

} /* namespace mockturtle */
<|endoftext|>


<commit_before>#include "../test.h"
#include <QUrl>
#include <QStateMachine>
#include <QCoreApplication>
#include <QtQuick/QQuickView>
#include <QKeyEvent>
#include <thread>
#include <chrono>
#include "../../src/input/scxml_importer.h"

class SignalSender : public QObject
{
  Q_OBJECT

signals:
  void signal();
};

class Test_ScxmlImporter : public ::testing::Test
{
 protected:
  virtual void SetUp()
  {
    int zero = 0;
    application = new QCoreApplication(zero, static_cast<char **>(nullptr));
    eventSender = new QObject();

    invokeManager = std::shared_ptr<InvokeManager>(new InvokeManager());
    signalManager = std::shared_ptr<SignalManager>(new SignalManager());
    signalManager->addSender(&sender);
    signalManager->addSender("KeyboardEventSender", eventSender);
    ScxmlImporter importer(QUrl::fromLocalFile("../config/simple_state.xml"),
                           invokeManager, signalManager);
    stateMachine = importer.import();

    stateMachine->start();

    application->processEvents(QEventLoop::ExcludeSocketNotifiers);
  }

  virtual void TearDown()
  {
    delete eventSender;
    delete application;
  }

  QAbstractState *expectSingleStateWithName(std::string name)
  {
    auto configuration = stateMachine->configuration();
    EXPECT_EQ(1, configuration.size());
    auto state = (*configuration.begin());
    EXPECT_EQ(name, state->property("name").toString().toStdString());

    return state;
  }

  void sendKeyPressEvent(Qt::Key key)
  {
    application->sendEvent(
        eventSender, new QKeyEvent(QEvent::KeyPress, key, Qt::NoModifier));
    application->processEvents();
  }

  void sendMouseButtonPressEvent(Qt::MouseButton button)
  {
    application->sendEvent(
        eventSender, new QMouseEvent(QEvent::MouseButtonPress, QPointF(0, 0),
                                     button, button, Qt::NoModifier));
    application->processEvents();
  }

  void sendKeyReleaseEvent(Qt::Key key)
  {
    application->sendEvent(
        eventSender, new QKeyEvent(QEvent::KeyRelease, key, Qt::NoModifier));
    application->processEvents();
  }

  void sendMouseButtonReleaseEvent(Qt::MouseButton button)
  {
    application->sendEvent(
        eventSender, new QMouseEvent(QEvent::MouseButtonRelease, QPointF(0, 0),
                                     button, button, Qt::NoModifier));
    application->processEvents();
  }

  void sendMouseMoveEvent(QPointF newPosition)
  {
    application->sendEvent(eventSender,
                           new QMouseEvent(QEvent::MouseMove, newPosition,
                                           Qt::MouseButton::NoButton,
                                           Qt::MouseButton::NoButton,
                                           Qt::NoModifier));
    application->processEvents();
  }

  QCoreApplication *application;
  QObject *eventSender;
  ScxmlImporter *importer;
  SignalSender sender;
  std::shared_ptr<QStateMachine> stateMachine;
  std::shared_ptr<InvokeManager> invokeManager;
  std::shared_ptr<SignalManager> signalManager;
};

TEST_F(Test_ScxmlImporter, SwitchFromInitialToFinalStateWithKeyPress)
{
  EXPECT_TRUE(stateMachine->isRunning());
  expectSingleStateWithName("idle");

  sendKeyPressEvent(Qt::Key_B);

  expectSingleStateWithName("exit");
  EXPECT_FALSE(stateMachine->isRunning());
}

class MockHandler : public QObject
{
  Q_OBJECT
 public slots:
  void printCurrentState()
  {
    printCurrentStateCallCount++;
  }

  void somethingElse()
  {
    somethingElseCallCount++;
  }

 public:
  int printCurrentStateCallCount = 0;
  int somethingElseCallCount = 0;
};

TEST_F(Test_ScxmlImporter,
       SwitchFromInitialToAnotherStateAndInvokeWithStateChange)
{
  MockHandler handler;
  invokeManager->addHandler("Window", &handler);

  sendKeyPressEvent(Qt::Key_V);

  expectSingleStateWithName("base");

  sendKeyPressEvent(Qt::Key_Y);

  expectSingleStateWithName("idle");
  EXPECT_TRUE(handler.printCurrentStateCallCount);
}

TEST_F(Test_ScxmlImporter,
       SwitchFromInitialToAnotherStateAndInvokeWithoutStateChange)
{
  MockHandler handler;
  invokeManager->addHandler("Window", &handler);

  sendKeyPressEvent(Qt::Key_V);

  expectSingleStateWithName("base");

  sendKeyPressEvent(Qt::Key_N);

  expectSingleStateWithName("base");
  EXPECT_TRUE(handler.printCurrentStateCallCount);
}

TEST_F(Test_ScxmlImporter, SwitchToNestedStateWithInitialElement)
{
  sendKeyPressEvent(Qt::Key_N);

  auto configuration = stateMachine->configuration();
  EXPECT_EQ(2, configuration.size());

  for (auto state : configuration)
  {
    auto name = state->property("name").toString().toStdString();
    EXPECT_TRUE(name == "nested" || name == "with-nesting");
  }
}

TEST_F(Test_ScxmlImporter, SwitchToNestedStateWithInitialElementAndInvoke)
{
  MockHandler handler;
  invokeManager->addHandler("Window", &handler);

  sendKeyPressEvent(Qt::Key_I);

  auto configuration = stateMachine->configuration();
  EXPECT_EQ(2, configuration.size());

  for (auto state : configuration)
  {
    auto name = state->property("name").toString().toStdString();
    EXPECT_TRUE(name == "nested-invoke" || name == "with-nesting-invoke");
  }

  EXPECT_TRUE(handler.printCurrentStateCallCount);
}

TEST_F(Test_ScxmlImporter, OnEntryInNestedState)
{
  MockHandler handler;
  invokeManager->addHandler("Window", &handler);

  sendKeyPressEvent(Qt::Key_E);

  expectSingleStateWithName("on-entry");

  EXPECT_TRUE(handler.printCurrentStateCallCount);
}

TEST_F(Test_ScxmlImporter, MultipleInvokesInOneTransition)
{
  MockHandler handler;
  invokeManager->addHandler("Window", &handler);

  sendKeyPressEvent(Qt::Key_J);

  EXPECT_EQ(1, handler.printCurrentStateCallCount);
  EXPECT_EQ(1, handler.somethingElseCallCount);
}

TEST_F(Test_ScxmlImporter, OnEntryWithMultipleInvokes)
{
  MockHandler handler;
  invokeManager->addHandler("Window", &handler);

  sendKeyPressEvent(Qt::Key_F);

  EXPECT_EQ(1, handler.printCurrentStateCallCount);
  EXPECT_EQ(1, handler.somethingElseCallCount);
}

TEST_F(Test_ScxmlImporter, OnExitInNestedState)
{
  MockHandler handler;
  invokeManager->addHandler("Window", &handler);

  sendKeyPressEvent(Qt::Key_G);

  expectSingleStateWithName("on-exit");

  sendKeyPressEvent(Qt::Key_G);

  EXPECT_EQ(1, handler.somethingElseCallCount);
}

TEST_F(Test_ScxmlImporter, TransitionOnKeyReleaseEvent)
{
  expectSingleStateWithName("idle");
  sendKeyPressEvent(Qt::Key_A);
  expectSingleStateWithName("idle");

  sendKeyReleaseEvent(Qt::Key_A);

  expectSingleStateWithName("exit");
}

TEST_F(Test_ScxmlImporter, TransitionOnMousePressEvent)
{
  expectSingleStateWithName("idle");

  sendMouseButtonPressEvent(Qt::MouseButton::LeftButton);

  expectSingleStateWithName("exit");
}

TEST_F(Test_ScxmlImporter, TransitionOnMouseReleaseEvent)
{
  expectSingleStateWithName("idle");

  sendMouseButtonReleaseEvent(Qt::MouseButton::RightButton);

  expectSingleStateWithName("base");
}

TEST_F(Test_ScxmlImporter, TransitionOnMouseMoveEvent)
{
  MockHandler handler;
  invokeManager->addHandler("Window", &handler);
  expectSingleStateWithName("idle");

  sendMouseMoveEvent(QPointF(123.1f, 56.0f));

  EXPECT_EQ(1, handler.somethingElseCallCount);
}

TEST_F(Test_ScxmlImporter, EventFromCustomSignal)
{
  MockHandler handler;
  invokeManager->addHandler("Window", &handler);
  expectSingleStateWithName("idle");

  emit sender.signal();

  expectSingleStateWithName("exit");
  EXPECT_EQ(1, handler.somethingElseCallCount);
}

TEST_F(Test_ScxmlImporter, StateChangeWithControlKey)
{
  expectSingleStateWithName("idle");

  sendKeyPressEvent(Qt::Key_Alt);

  expectSingleStateWithName("alt");
}

#include "test_scxml_importer.moc"
<commit_msg>Fix tests by adjusting path to simple_state.xml.<commit_after>#include "../test.h"
#include <QUrl>
#include <QStateMachine>
#include <QCoreApplication>
#include <QtQuick/QQuickView>
#include <QKeyEvent>
#include <thread>
#include <chrono>
#include "../../src/input/scxml_importer.h"

class SignalSender : public QObject
{
  Q_OBJECT

signals:
  void signal();
};

class Test_ScxmlImporter : public ::testing::Test
{
 protected:
  virtual void SetUp()
  {
    int zero = 0;
    application = new QCoreApplication(zero, static_cast<char **>(nullptr));
    eventSender = new QObject();

    invokeManager = std::shared_ptr<InvokeManager>(new InvokeManager());
    signalManager = std::shared_ptr<SignalManager>(new SignalManager());
    signalManager->addSender(&sender);
    signalManager->addSender("KeyboardEventSender", eventSender);
    ScxmlImporter importer(QUrl::fromLocalFile("config/simple_state.xml"),
                           invokeManager, signalManager);
    stateMachine = importer.import();

    stateMachine->start();

    application->processEvents(QEventLoop::ExcludeSocketNotifiers);
  }

  virtual void TearDown()
  {
    delete eventSender;
    delete application;
  }

  QAbstractState *expectSingleStateWithName(std::string name)
  {
    auto configuration = stateMachine->configuration();
    EXPECT_EQ(1, configuration.size());
    auto state = (*configuration.begin());
    EXPECT_EQ(name, state->property("name").toString().toStdString());

    return state;
  }

  void sendKeyPressEvent(Qt::Key key)
  {
    application->sendEvent(
        eventSender, new QKeyEvent(QEvent::KeyPress, key, Qt::NoModifier));
    application->processEvents();
  }

  void sendMouseButtonPressEvent(Qt::MouseButton button)
  {
    application->sendEvent(
        eventSender, new QMouseEvent(QEvent::MouseButtonPress, QPointF(0, 0),
                                     button, button, Qt::NoModifier));
    application->processEvents();
  }

  void sendKeyReleaseEvent(Qt::Key key)
  {
    application->sendEvent(
        eventSender, new QKeyEvent(QEvent::KeyRelease, key, Qt::NoModifier));
    application->processEvents();
  }

  void sendMouseButtonReleaseEvent(Qt::MouseButton button)
  {
    application->sendEvent(
        eventSender, new QMouseEvent(QEvent::MouseButtonRelease, QPointF(0, 0),
                                     button, button, Qt::NoModifier));
    application->processEvents();
  }

  void sendMouseMoveEvent(QPointF newPosition)
  {
    application->sendEvent(eventSender,
                           new QMouseEvent(QEvent::MouseMove, newPosition,
                                           Qt::MouseButton::NoButton,
                                           Qt::MouseButton::NoButton,
                                           Qt::NoModifier));
    application->processEvents();
  }

  QCoreApplication *application;
  QObject *eventSender;
  ScxmlImporter *importer;
  SignalSender sender;
  std::shared_ptr<QStateMachine> stateMachine;
  std::shared_ptr<InvokeManager> invokeManager;
  std::shared_ptr<SignalManager> signalManager;
};

TEST_F(Test_ScxmlImporter, SwitchFromInitialToFinalStateWithKeyPress)
{
  EXPECT_TRUE(stateMachine->isRunning());
  expectSingleStateWithName("idle");

  sendKeyPressEvent(Qt::Key_B);

  expectSingleStateWithName("exit");
  EXPECT_FALSE(stateMachine->isRunning());
}

class MockHandler : public QObject
{
  Q_OBJECT
 public slots:
  void printCurrentState()
  {
    printCurrentStateCallCount++;
  }

  void somethingElse()
  {
    somethingElseCallCount++;
  }

 public:
  int printCurrentStateCallCount = 0;
  int somethingElseCallCount = 0;
};

TEST_F(Test_ScxmlImporter,
       SwitchFromInitialToAnotherStateAndInvokeWithStateChange)
{
  MockHandler handler;
  invokeManager->addHandler("Window", &handler);

  sendKeyPressEvent(Qt::Key_V);

  expectSingleStateWithName("base");

  sendKeyPressEvent(Qt::Key_Y);

  expectSingleStateWithName("idle");
  EXPECT_TRUE(handler.printCurrentStateCallCount);
}

TEST_F(Test_ScxmlImporter,
       SwitchFromInitialToAnotherStateAndInvokeWithoutStateChange)
{
  MockHandler handler;
  invokeManager->addHandler("Window", &handler);

  sendKeyPressEvent(Qt::Key_V);

  expectSingleStateWithName("base");

  sendKeyPressEvent(Qt::Key_N);

  expectSingleStateWithName("base");
  EXPECT_TRUE(handler.printCurrentStateCallCount);
}

TEST_F(Test_ScxmlImporter, SwitchToNestedStateWithInitialElement)
{
  sendKeyPressEvent(Qt::Key_N);

  auto configuration = stateMachine->configuration();
  EXPECT_EQ(2, configuration.size());

  for (auto state : configuration)
  {
    auto name = state->property("name").toString().toStdString();
    EXPECT_TRUE(name == "nested" || name == "with-nesting");
  }
}

TEST_F(Test_ScxmlImporter, SwitchToNestedStateWithInitialElementAndInvoke)
{
  MockHandler handler;
  invokeManager->addHandler("Window", &handler);

  sendKeyPressEvent(Qt::Key_I);

  auto configuration = stateMachine->configuration();
  EXPECT_EQ(2, configuration.size());

  for (auto state : configuration)
  {
    auto name = state->property("name").toString().toStdString();
    EXPECT_TRUE(name == "nested-invoke" || name == "with-nesting-invoke");
  }

  EXPECT_TRUE(handler.printCurrentStateCallCount);
}

TEST_F(Test_ScxmlImporter, OnEntryInNestedState)
{
  MockHandler handler;
  invokeManager->addHandler("Window", &handler);

  sendKeyPressEvent(Qt::Key_E);

  expectSingleStateWithName("on-entry");

  EXPECT_TRUE(handler.printCurrentStateCallCount);
}

TEST_F(Test_ScxmlImporter, MultipleInvokesInOneTransition)
{
  MockHandler handler;
  invokeManager->addHandler("Window", &handler);

  sendKeyPressEvent(Qt::Key_J);

  EXPECT_EQ(1, handler.printCurrentStateCallCount);
  EXPECT_EQ(1, handler.somethingElseCallCount);
}

TEST_F(Test_ScxmlImporter, OnEntryWithMultipleInvokes)
{
  MockHandler handler;
  invokeManager->addHandler("Window", &handler);

  sendKeyPressEvent(Qt::Key_F);

  EXPECT_EQ(1, handler.printCurrentStateCallCount);
  EXPECT_EQ(1, handler.somethingElseCallCount);
}

TEST_F(Test_ScxmlImporter, OnExitInNestedState)
{
  MockHandler handler;
  invokeManager->addHandler("Window", &handler);

  sendKeyPressEvent(Qt::Key_G);

  expectSingleStateWithName("on-exit");

  sendKeyPressEvent(Qt::Key_G);

  EXPECT_EQ(1, handler.somethingElseCallCount);
}

TEST_F(Test_ScxmlImporter, TransitionOnKeyReleaseEvent)
{
  expectSingleStateWithName("idle");
  sendKeyPressEvent(Qt::Key_A);
  expectSingleStateWithName("idle");

  sendKeyReleaseEvent(Qt::Key_A);

  expectSingleStateWithName("exit");
}

TEST_F(Test_ScxmlImporter, TransitionOnMousePressEvent)
{
  expectSingleStateWithName("idle");

  sendMouseButtonPressEvent(Qt::MouseButton::LeftButton);

  expectSingleStateWithName("exit");
}

TEST_F(Test_ScxmlImporter, TransitionOnMouseReleaseEvent)
{
  expectSingleStateWithName("idle");

  sendMouseButtonReleaseEvent(Qt::MouseButton::RightButton);

  expectSingleStateWithName("base");
}

TEST_F(Test_ScxmlImporter, TransitionOnMouseMoveEvent)
{
  MockHandler handler;
  invokeManager->addHandler("Window", &handler);
  expectSingleStateWithName("idle");

  sendMouseMoveEvent(QPointF(123.1f, 56.0f));

  EXPECT_EQ(1, handler.somethingElseCallCount);
}

TEST_F(Test_ScxmlImporter, EventFromCustomSignal)
{
  MockHandler handler;
  invokeManager->addHandler("Window", &handler);
  expectSingleStateWithName("idle");

  emit sender.signal();

  expectSingleStateWithName("exit");
  EXPECT_EQ(1, handler.somethingElseCallCount);
}

TEST_F(Test_ScxmlImporter, StateChangeWithControlKey)
{
  expectSingleStateWithName("idle");

  sendKeyPressEvent(Qt::Key_Alt);

  expectSingleStateWithName("alt");
}

#include "test_scxml_importer.moc"
<|endoftext|>


<commit_before>#include <cstdio>
#include <string>
#include <regex>
#include <fstream>
#include <sys/stat.h>
// http://tclap.sourceforge.net/manual.html
#include "tclap/CmdLine.h"
#include "HCIDumpParser.h"
#include "MsgPublisherTypeConstraint.h"

static HCIDumpParser parserLogic;

#define STOP_MARKER_FILE "STOP"

inline bool stopMarkerExists() {
    struct stat buffer;
    bool stop = (stat (STOP_MARKER_FILE, &buffer) == 0);
    if(stop) {
        printf("Found STOP marker file, will exit...\n");
    }
    return stop;
}
static long eventCount = 0;
static int64_t lastMarkerCheckTime = 0;

/**
* Callback invoked by the hdidumpinternal.c code when a LE_ADVERTISING_REPORT event is seen on the stack
*/
extern "C" bool beacon_event_callback(const beacon_info *info) {
#ifdef PRINT_DEBUG
    printf("beacon_event_callback(%s, code=%d, time=%lld)\n", info->uuid, info->code, info->time);
#endif
    parserLogic.beaconEvent(info);
    eventCount ++;
    // Check for a termination marker every 1000 events or 5 seconds
    bool stop = false;
    int64_t elapsed = info->time - lastMarkerCheckTime;
    if((eventCount % 1000) == 0 || elapsed > 5000) {
        lastMarkerCheckTime = info->time;
        stop = stopMarkerExists();
    }
    return stop;
}

using namespace std;

/**
* A version of the native scanner that directly integrates with the bluez stack hcidump command rather than parsing
* the hcidump output.
*/
int main(int argc, const char **argv) {

    printf("NativeScanner starting up...\n");
    TCLAP::CmdLine cmd("NativeScanner command line options", ' ', "0.1");
    //
    TCLAP::ValueArg<std::string> scannerID("s", "scannerID",
            "Specify the ID of the scanner generating the beacon events",
            true, "DEFAULT", "string", cmd);
    TCLAP::ValueArg<std::string> rawDumpFile("d", "rawDumpFile",
            "Specify a path to an hcidump file to parse for testing",
            false, "", "string", cmd);
    TCLAP::ValueArg<std::string> clientID("c", "clientID",
            "Specify the clientID to connect to the MQTT broker with",
            false, "", "string", cmd);
    TCLAP::ValueArg<std::string> username("u", "username",
            "Specify the username to connect to the MQTT broker with",
            false, "", "string", cmd);
    TCLAP::ValueArg<std::string> password("p", "password",
            "Specify the password to connect to the MQTT broker with",
            false, "", "string", cmd);
    TCLAP::ValueArg<std::string> brokerURL("b", "brokerURL",
            "Specify the brokerURL to connect to the MQTT broker with; default tcp://localhost:1883",
            false, "tcp://localhost:1883", "string", cmd);
    TCLAP::ValueArg<std::string> topicName("t", "destinationName",
            "Specify the name of the queue on the MQTT broker to publish to; default beaconEvents",
            false, "beaconEvents", "string", cmd);
    TCLAP::SwitchArg skipPublish("S", "skipPublish",
            "Indicate that the parsed beacons should not be published",
            false);
    TCLAP::SwitchArg asyncMode("A", "asyncMode",
            "Indicate that the parsed beacons should be published using async delivery mode",
            false);
    TCLAP::SwitchArg useQueues("Q", "useQueues",
            "Indicate that the destination type is a queue. If not given the default type is a topic.",
            false);
    TCLAP::ValueArg<std::string> hciDev("D", "hciDev",
            "Specify the name of the host controller interface to use; default hci0",
            false, "hci0", "string", cmd);
    MsgPublisherTypeConstraint pubTypeConstraint;
    TCLAP::ValueArg<std::string> pubType("P", "pubType",
            "Specify the MsgPublisherType enum for the publisher implementation to use; default PAHO_MQTT",
            false, "PAHO_MQTT", &pubTypeConstraint, cmd, nullptr);

    try {
        // Add the flag arguments
        cmd.add(skipPublish);
        cmd.add(asyncMode);
        cmd.add(useQueues);
        // Parse the argv array.
        printf("Parsing command line...\n");
        cmd.parse( argc, argv );
        printf("done\n");
    }
    catch (TCLAP::ArgException &e) {
        fprintf(stderr, "error: %s for arg: %s\n", e.error().c_str(), e.argId().c_str());
    }

    // Remove any stop marker file
    if(remove(STOP_MARKER_FILE) == 0) {
        printf("Removed existing %s marker file\n", STOP_MARKER_FILE);
    }

    HCIDumpCommand command(scannerID.getValue(), brokerURL.getValue(), clientID.getValue(), topicName.getValue());
    command.setSkipPublish(skipPublish.getValue());
    command.setHciDev(hciDev.getValue());
    command.setAsyncMode(asyncMode.getValue());
    command.setPubType(pubTypeConstraint.toType(pubType.getValue()));
    printf("Begin scanning...\n");
    parserLogic.processHCI(command);
    parserLogic.cleanup();
    printf("End scanning\n");
    return 0;
}
<commit_msg>Include event count in callback debug statement<commit_after>#include <cstdio>
#include <string>
#include <regex>
#include <fstream>
#include <sys/stat.h>
// http://tclap.sourceforge.net/manual.html
#include "tclap/CmdLine.h"
#include "HCIDumpParser.h"
#include "MsgPublisherTypeConstraint.h"

static HCIDumpParser parserLogic;

#define STOP_MARKER_FILE "STOP"

inline bool stopMarkerExists() {
    struct stat buffer;
    bool stop = (stat (STOP_MARKER_FILE, &buffer) == 0);
    if(stop) {
        printf("Found STOP marker file, will exit...\n");
    }
    return stop;
}
static long eventCount = 0;
static int64_t lastMarkerCheckTime = 0;

/**
* Callback invoked by the hdidumpinternal.c code when a LE_ADVERTISING_REPORT event is seen on the stack
*/
extern "C" bool beacon_event_callback(const beacon_info *info) {
#ifdef PRINT_DEBUG
    printf("beacon_event_callback(%ld: %s, code=%d, time=%lld)\n", eventCount, info->uuid, info->code, info->time);
#endif
    parserLogic.beaconEvent(info);
    eventCount ++;
    // Check for a termination marker every 1000 events or 5 seconds
    bool stop = false;
    int64_t elapsed = info->time - lastMarkerCheckTime;
    if((eventCount % 1000) == 0 || elapsed > 5000) {
        lastMarkerCheckTime = info->time;
        stop = stopMarkerExists();
    }
    return stop;
}

using namespace std;

/**
* A version of the native scanner that directly integrates with the bluez stack hcidump command rather than parsing
* the hcidump output.
*/
int main(int argc, const char **argv) {

    printf("NativeScanner starting up...\n");
    TCLAP::CmdLine cmd("NativeScanner command line options", ' ', "0.1");
    //
    TCLAP::ValueArg<std::string> scannerID("s", "scannerID",
            "Specify the ID of the scanner generating the beacon events",
            true, "DEFAULT", "string", cmd);
    TCLAP::ValueArg<std::string> rawDumpFile("d", "rawDumpFile",
            "Specify a path to an hcidump file to parse for testing",
            false, "", "string", cmd);
    TCLAP::ValueArg<std::string> clientID("c", "clientID",
            "Specify the clientID to connect to the MQTT broker with",
            false, "", "string", cmd);
    TCLAP::ValueArg<std::string> username("u", "username",
            "Specify the username to connect to the MQTT broker with",
            false, "", "string", cmd);
    TCLAP::ValueArg<std::string> password("p", "password",
            "Specify the password to connect to the MQTT broker with",
            false, "", "string", cmd);
    TCLAP::ValueArg<std::string> brokerURL("b", "brokerURL",
            "Specify the brokerURL to connect to the MQTT broker with; default tcp://localhost:1883",
            false, "tcp://localhost:1883", "string", cmd);
    TCLAP::ValueArg<std::string> topicName("t", "destinationName",
            "Specify the name of the queue on the MQTT broker to publish to; default beaconEvents",
            false, "beaconEvents", "string", cmd);
    TCLAP::SwitchArg skipPublish("S", "skipPublish",
            "Indicate that the parsed beacons should not be published",
            false);
    TCLAP::SwitchArg asyncMode("A", "asyncMode",
            "Indicate that the parsed beacons should be published using async delivery mode",
            false);
    TCLAP::SwitchArg useQueues("Q", "useQueues",
            "Indicate that the destination type is a queue. If not given the default type is a topic.",
            false);
    TCLAP::ValueArg<std::string> hciDev("D", "hciDev",
            "Specify the name of the host controller interface to use; default hci0",
            false, "hci0", "string", cmd);
    MsgPublisherTypeConstraint pubTypeConstraint;
    TCLAP::ValueArg<std::string> pubType("P", "pubType",
            "Specify the MsgPublisherType enum for the publisher implementation to use; default PAHO_MQTT",
            false, "PAHO_MQTT", &pubTypeConstraint, cmd, nullptr);

    try {
        // Add the flag arguments
        cmd.add(skipPublish);
        cmd.add(asyncMode);
        cmd.add(useQueues);
        // Parse the argv array.
        printf("Parsing command line...\n");
        cmd.parse( argc, argv );
        printf("done\n");
    }
    catch (TCLAP::ArgException &e) {
        fprintf(stderr, "error: %s for arg: %s\n", e.error().c_str(), e.argId().c_str());
    }

    // Remove any stop marker file
    if(remove(STOP_MARKER_FILE) == 0) {
        printf("Removed existing %s marker file\n", STOP_MARKER_FILE);
    }

    HCIDumpCommand command(scannerID.getValue(), brokerURL.getValue(), clientID.getValue(), topicName.getValue());
    command.setSkipPublish(skipPublish.getValue());
    command.setHciDev(hciDev.getValue());
    command.setAsyncMode(asyncMode.getValue());
    command.setPubType(pubTypeConstraint.toType(pubType.getValue()));
    printf("Begin scanning...\n");
    parserLogic.processHCI(command);
    parserLogic.cleanup();
    printf("End scanning\n");
    return 0;
}
<|endoftext|>


<commit_before>#include "fence_manager.hpp"

#include "../base/assert.hpp"
#include "../base/logging.hpp"

FenceManager::FenceManager(int conditionPoolSize)
  : m_currentFence(0),
    m_isCancelled(false)
{
  for (unsigned i = 0; i < conditionPoolSize; ++i)
    m_conditionPool.push_back(new threads::Condition());
}

FenceManager::~FenceManager()
{
  for (map<int, threads::Condition*>::const_iterator it = m_activeFences.begin();
       it != m_activeFences.end();
       ++it)
  {
    it->second->Signal();
    delete it->second;
  }

  for (list<threads::Condition*>::iterator it = m_conditionPool.begin(); it != m_conditionPool.end(); ++it)
    delete *it;
}

int FenceManager::insertFence()
{
  threads::MutexGuard g(m_mutex);

  if (m_isCancelled)
    return -1;

  if (m_conditionPool.empty())
    return -1;

  threads::Condition * cond = m_conditionPool.front();
  m_conditionPool.pop_front();

  int id = m_currentFence++;

  m_activeFences[id] = cond;

  return id;
}

void FenceManager::signalFence(int id)
{
  threads::MutexGuard g(m_mutex);

  if (m_isCancelled)
    return;

  map<int, threads::Condition*>::iterator it = m_activeFences.find(id);

  if (it == m_activeFences.end())
  {
    LOG(LINFO, ("fence with id", id, "has been already signalled or hasn't been installed yet"));
    return;
  }

  threads::Condition * cond = it->second;

  /// erasing fence from active fences
  m_activeFences.erase(it);

  /// returning condition to the pool
  m_conditionPool.push_back(cond);

  /// signalling to all waiting fences
  cond->Signal(true);
}

void FenceManager::joinFence(int id)
{
  threads::Condition * cond = 0;
  {
    threads::MutexGuard g(m_mutex);

    if (m_isCancelled)
      return;

    map<int, threads::Condition*>::iterator it = m_activeFences.find(id);

    if (it == m_activeFences.end())
    {
      LOG(LINFO, ("fence with id", id, "has been already reached in the past or hasn't been installed yet"));
      return;
    }

    cond = it->second;
  }

  threads::ConditionGuard g(*cond);
  g.Wait();
}

void FenceManager::cancel()
{
  threads::MutexGuard g(m_mutex);

  m_isCancelled = true;

  map<int, threads::Condition*>::iterator it = m_activeFences.begin();

  for (; it != m_activeFences.end(); ++it)
    it->second->Signal(true);
}
<commit_msg>fixed deadlock issue in joinFence function.<commit_after>#include "fence_manager.hpp"

#include "../base/assert.hpp"
#include "../base/logging.hpp"

FenceManager::FenceManager(int conditionPoolSize)
  : m_currentFence(0),
    m_isCancelled(false)
{
  for (unsigned i = 0; i < conditionPoolSize; ++i)
    m_conditionPool.push_back(new threads::Condition());
}

FenceManager::~FenceManager()
{
  for (map<int, threads::Condition*>::const_iterator it = m_activeFences.begin();
       it != m_activeFences.end();
       ++it)
  {
    it->second->Signal();
    delete it->second;
  }

  for (list<threads::Condition*>::iterator it = m_conditionPool.begin(); it != m_conditionPool.end(); ++it)
    delete *it;
}

int FenceManager::insertFence()
{
  threads::MutexGuard g(m_mutex);

  if (m_isCancelled)
    return -1;

  if (m_conditionPool.empty())
    return -1;

  threads::Condition * cond = m_conditionPool.front();
  m_conditionPool.pop_front();

  int id = m_currentFence++;

  m_activeFences[id] = cond;

  return id;
}

void FenceManager::signalFence(int id)
{
  threads::MutexGuard g(m_mutex);

  if (m_isCancelled)
    return;

  map<int, threads::Condition*>::iterator it = m_activeFences.find(id);

  if (it == m_activeFences.end())
  {
    LOG(LINFO, ("fence with id", id, "has been already signalled or hasn't been installed yet"));
    return;
  }

  threads::Condition * cond = it->second;

  threads::ConditionGuard fenceGuard(*cond);

  /// erasing fence from active fences
  m_activeFences.erase(it);

  /// returning condition to the pool
  m_conditionPool.push_back(cond);

  /// signalling to all waiting fences
  fenceGuard.Signal(true);
}

void FenceManager::joinFence(int id)
{
  threads::Condition * cond = 0;
  {
    threads::MutexGuard g(m_mutex);

    if (m_isCancelled)
      return;

    map<int, threads::Condition*>::iterator it = m_activeFences.find(id);

    if (it == m_activeFences.end())
    {
      LOG(LINFO, ("fence with id", id, "has been already reached in the past or hasn't been installed yet"));
      return;
    }

    cond = it->second;
  }

  threads::ConditionGuard fenceGuard(*cond);
  if (m_activeFences.find(id) != m_activeFences.end())
    fenceGuard.Wait();
}

void FenceManager::cancel()
{
  threads::MutexGuard g(m_mutex);

  m_isCancelled = true;

  list<pair<int, threads::Condition*> > tempList;

  for (map<int, threads::Condition*>::iterator it = m_activeFences.begin();
       it != m_activeFences.end();
       ++it)
       tempList.push_back(make_pair(it->first, it->second));

  for (list<pair<int, threads::Condition*> >::const_iterator it = tempList.begin();
       it != tempList.end();
       ++it)
  {
    threads::ConditionGuard fenceGuard(*it->second);
    m_activeFences.erase(it->first);
    fenceGuard.Signal(true);
  }
}
<|endoftext|>


<commit_before>/*
 * This file is open source software, licensed to you under the terms
 * of the Apache License, Version 2.0 (the "License").  See the NOTICE file
 * distributed with this work for additional information regarding copyright
 * ownership.  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.
 */

/*
 * Copyright (C) 2015 ScyllaDB
 */

#pragma once

#include <seastar/core/future.hh>
#include <seastar/core/expiring_fifo.hh>

/// \addtogroup future-module
/// @{

namespace seastar {

/// Changes the clock used by shared_future<> and shared_promise<> when passed as the first template parameter.
template<typename Clock>
struct with_clock {};

/// \cond internal

template <typename... T>
struct future_option_traits;

template <typename Clock, typename... T>
struct future_option_traits<with_clock<Clock>, T...> {
    using clock_type = Clock;

    template<template <typename...> class Class>
    struct parametrize {
        using type = Class<T...>;
    };
};

template <typename... T>
struct future_option_traits {
    using clock_type = lowres_clock;

    template<template <typename...> class Class>
    struct parametrize {
        using type = Class<T...>;
    };
};

/// \endcond

/// \brief Like \ref future except the result can be waited for by many fibers.
///
/// Represents a value which may not yet be ready. A fiber can wait for the value using
/// the \ref future obtained by calling \ref get_future() or casting to \ref future type.
/// Multiple fibers are allowed to obtain a \ref future for the result using the same
/// instance of \ref shared_future.
///
/// All futures obtained from shared_future should end up in the same state. However,
/// if the value's copy constructor throws, some of the futures may end up in a failed state
/// with an exception thrown from the copy constructor and end up with a state
/// different than other futures.
///
/// The scope of shared_future instance doesn't have to include scopes of the futures
/// obtained from that instance. In that sense the returned futures are independent.
///
/// shared_future can be copied at any time and all copies will resolve with the same value.
///
/// shared_future can be in a disengaged state when it's default-constructed or moved-from.
/// When it's in such a state we say it's invalid and obtaining futures must not be attempted.
///
/// The types in the parameter pack T must all be copy-constructible.
///
/// When the first type in the parameter pack is \ref with_clock then it has the effect
/// of changing the clock used for timeouts by this instance. This type is omitted from
/// the parameter of the future<> objects.
///
/// Example:
///
///    future<int> f;
///    shared_future<with_clock<manual_clock>, int> sf(std::move(f));
///    future<int> f2 = sf;
///
template<typename... T>
class shared_future {
    template <typename... U> friend class shared_promise;
    using options = future_option_traits<T...>;
public:
    using clock = typename options::clock_type;
    using time_point = typename clock::time_point;
    using future_type = typename future_option_traits<T...>::template parametrize<future>::type;
    using promise_type = typename future_option_traits<T...>::template parametrize<promise>::type;
    using value_tuple_type = typename future_option_traits<T...>::template parametrize<std::tuple>::type;
private:
    using future_state_type = typename future_option_traits<T...>::template parametrize<future_state>::type;
    using promise_expiry = typename future_option_traits<T...>::template parametrize<promise_expiry>::type;

    /// \cond internal
    class shared_state {
        future_state_type _future_state;
        expiring_fifo<promise_type, promise_expiry, clock> _peers;
    public:
        void resolve(future_type&& f) noexcept {
            _future_state = f.get_available_state();
            if (_future_state.failed()) {
                while (_peers) {
                    _peers.front().set_exception(_future_state.get_exception());
                    _peers.pop_front();
                }
            } else {
                while (_peers) {
                    auto& p = _peers.front();
                    try {
                        p.set_value(_future_state.get_value());
                    } catch (...) {
                        p.set_exception(std::current_exception());
                    }
                    _peers.pop_front();
                }
            }
        }

        future_type get_future(time_point timeout = time_point::max()) {
            if (!_future_state.available()) {
                promise_type p;
                auto f = p.get_future();
                _peers.push_back(std::move(p), timeout);
                return f;
            } else if (_future_state.failed()) {
                return future_type(exception_future_marker(), _future_state.get_exception());
            } else {
                try {
                    return future_type(ready_future_marker(), _future_state.get_value());
                } catch (...) {
                    return future_type(exception_future_marker(), std::current_exception());
                }
            }
        }

        bool available() const noexcept {
            return _future_state.available();
        }

        bool failed() const noexcept {
            return _future_state.failed();
        }
    };
    /// \endcond
    lw_shared_ptr<shared_state> _state;
public:
    /// \brief Forwards the result of future \c f into this shared_future.
    shared_future(future_type&& f)
        : _state(make_lw_shared<shared_state>())
    {
        // future's result is forwarded to `_state`.
        f.then_wrapped([s = _state] (future_type&& f) mutable {
            s->resolve(std::move(f));
        });
    }

    shared_future() = default;
    shared_future(const shared_future&) = default;
    shared_future& operator=(const shared_future&) = default;
    shared_future(shared_future&&) = default;
    shared_future& operator=(shared_future&&) = default;

    /// \brief Creates a new \c future which will resolve with the result of this shared_future
    ///
    /// \param timeout When engaged, the returned future will resolve with \ref timed_out_error
    /// if this shared_future doesn't resolve before timeout is reached.
    ///
    /// This object must be in a valid state.
    future_type get_future(time_point timeout = time_point::max()) const {
        return _state->get_future(timeout);
    }

    /// \brief Returns true if the future is available (ready or failed)
    ///
    /// \note This object must be in a valid state.
    bool available() const noexcept {
        return _state->available();
    }

    /// \brief Returns true if the future is failed
    ///
    /// \note This object must be in a valid state.
    bool failed() const noexcept {
        return _state->failed();
    }

    /// \brief Equivalent to \ref get_future()
    operator future_type() const {
        return get_future();
    }

    /// \brief Returns true if the instance is in valid state
    bool valid() const {
        return bool(_state);
    }
};

/// \brief Like \ref promise except that its counterpart is \ref shared_future instead of \ref future
///
/// When the shared_promise is made ready, every waiter is also made ready.
///
/// Like the shared_future, the types in the parameter pack T must all be copy-constructible.
template <typename... T>
class shared_promise {
public:
    using shared_future_type = shared_future<T...>;
    using future_type = typename shared_future_type::future_type;
    using promise_type = typename shared_future_type::promise_type;
    using clock = typename shared_future_type::clock;
    using time_point = typename shared_future_type::time_point;
    using value_tuple_type = typename shared_future_type::value_tuple_type;
    using future_state_type = typename shared_future_type::future_state_type;
private:
    promise_type _promise;
    shared_future_type _shared_future;
    static constexpr bool copy_noexcept = future_state_type::copy_noexcept;
public:
    shared_promise(const shared_promise&) = delete;
    shared_promise(shared_promise&&) = default;
    shared_promise& operator=(shared_promise&&) = default;
    shared_promise() : _promise(), _shared_future(_promise.get_future()) {
    }

    /// \brief Gets new future associated with this promise.
    /// If the promise is not resolved before timeout the returned future will resolve with \ref timed_out_error.
    /// This instance doesn't have to be kept alive until the returned future resolves.
    future_type get_shared_future(time_point timeout = time_point::max()) const {
        return _shared_future.get_future(timeout);
    }

    /// \brief Sets the shared_promise's value (as tuple; by copying), same as normal promise
    void set_value(const value_tuple_type& result) noexcept(copy_noexcept) {
        _promise.set_value(result);
    }

    /// \brief Sets the shared_promise's value (as tuple; by moving), same as normal promise
    void set_value(value_tuple_type&& result) noexcept {
        _promise.set_value(std::move(result));
    }

    /// \brief Sets the shared_promise's value (variadic), same as normal promise
    template <typename... A>
    void set_value(A&&... a) noexcept {
        _promise.set_value(std::forward<A>(a)...);
    }

    /// \brief Marks the shared_promise as failed, same as normal promise
    void set_exception(std::exception_ptr ex) noexcept {
        _promise.set_exception(std::move(ex));
    }

    /// \brief Marks the shared_promise as failed, same as normal promise
    template<typename Exception>
    void set_exception(Exception&& e) noexcept {
        set_exception(make_exception_ptr(std::forward<Exception>(e)));
    }

    /// \brief Returns true if the underlying future is available (ready or failed)
    bool available() const noexcept {
        return _shared_future.available();
    }

    /// \brief Returns true if the underlying future is  failed
    bool failed() const noexcept {
        return _shared_future.failed();
    }
};

/// @}

}
<commit_msg>shared_future: silence warning about discarded future<commit_after>/*
 * This file is open source software, licensed to you under the terms
 * of the Apache License, Version 2.0 (the "License").  See the NOTICE file
 * distributed with this work for additional information regarding copyright
 * ownership.  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.
 */

/*
 * Copyright (C) 2015 ScyllaDB
 */

#pragma once

#include <seastar/core/future.hh>
#include <seastar/core/expiring_fifo.hh>

/// \addtogroup future-module
/// @{

namespace seastar {

/// Changes the clock used by shared_future<> and shared_promise<> when passed as the first template parameter.
template<typename Clock>
struct with_clock {};

/// \cond internal

template <typename... T>
struct future_option_traits;

template <typename Clock, typename... T>
struct future_option_traits<with_clock<Clock>, T...> {
    using clock_type = Clock;

    template<template <typename...> class Class>
    struct parametrize {
        using type = Class<T...>;
    };
};

template <typename... T>
struct future_option_traits {
    using clock_type = lowres_clock;

    template<template <typename...> class Class>
    struct parametrize {
        using type = Class<T...>;
    };
};

/// \endcond

/// \brief Like \ref future except the result can be waited for by many fibers.
///
/// Represents a value which may not yet be ready. A fiber can wait for the value using
/// the \ref future obtained by calling \ref get_future() or casting to \ref future type.
/// Multiple fibers are allowed to obtain a \ref future for the result using the same
/// instance of \ref shared_future.
///
/// All futures obtained from shared_future should end up in the same state. However,
/// if the value's copy constructor throws, some of the futures may end up in a failed state
/// with an exception thrown from the copy constructor and end up with a state
/// different than other futures.
///
/// The scope of shared_future instance doesn't have to include scopes of the futures
/// obtained from that instance. In that sense the returned futures are independent.
///
/// shared_future can be copied at any time and all copies will resolve with the same value.
///
/// shared_future can be in a disengaged state when it's default-constructed or moved-from.
/// When it's in such a state we say it's invalid and obtaining futures must not be attempted.
///
/// The types in the parameter pack T must all be copy-constructible.
///
/// When the first type in the parameter pack is \ref with_clock then it has the effect
/// of changing the clock used for timeouts by this instance. This type is omitted from
/// the parameter of the future<> objects.
///
/// Example:
///
///    future<int> f;
///    shared_future<with_clock<manual_clock>, int> sf(std::move(f));
///    future<int> f2 = sf;
///
template<typename... T>
class shared_future {
    template <typename... U> friend class shared_promise;
    using options = future_option_traits<T...>;
public:
    using clock = typename options::clock_type;
    using time_point = typename clock::time_point;
    using future_type = typename future_option_traits<T...>::template parametrize<future>::type;
    using promise_type = typename future_option_traits<T...>::template parametrize<promise>::type;
    using value_tuple_type = typename future_option_traits<T...>::template parametrize<std::tuple>::type;
private:
    using future_state_type = typename future_option_traits<T...>::template parametrize<future_state>::type;
    using promise_expiry = typename future_option_traits<T...>::template parametrize<promise_expiry>::type;

    /// \cond internal
    class shared_state {
        future_state_type _future_state;
        expiring_fifo<promise_type, promise_expiry, clock> _peers;
    public:
        void resolve(future_type&& f) noexcept {
            _future_state = f.get_available_state();
            if (_future_state.failed()) {
                while (_peers) {
                    _peers.front().set_exception(_future_state.get_exception());
                    _peers.pop_front();
                }
            } else {
                while (_peers) {
                    auto& p = _peers.front();
                    try {
                        p.set_value(_future_state.get_value());
                    } catch (...) {
                        p.set_exception(std::current_exception());
                    }
                    _peers.pop_front();
                }
            }
        }

        future_type get_future(time_point timeout = time_point::max()) {
            if (!_future_state.available()) {
                promise_type p;
                auto f = p.get_future();
                _peers.push_back(std::move(p), timeout);
                return f;
            } else if (_future_state.failed()) {
                return future_type(exception_future_marker(), _future_state.get_exception());
            } else {
                try {
                    return future_type(ready_future_marker(), _future_state.get_value());
                } catch (...) {
                    return future_type(exception_future_marker(), std::current_exception());
                }
            }
        }

        bool available() const noexcept {
            return _future_state.available();
        }

        bool failed() const noexcept {
            return _future_state.failed();
        }
    };
    /// \endcond
    lw_shared_ptr<shared_state> _state;
public:
    /// \brief Forwards the result of future \c f into this shared_future.
    shared_future(future_type&& f)
        : _state(make_lw_shared<shared_state>())
    {
        // future's result is forwarded to `_state`.
        (void)f.then_wrapped([s = _state] (future_type&& f) mutable {
            s->resolve(std::move(f));
        });
    }

    shared_future() = default;
    shared_future(const shared_future&) = default;
    shared_future& operator=(const shared_future&) = default;
    shared_future(shared_future&&) = default;
    shared_future& operator=(shared_future&&) = default;

    /// \brief Creates a new \c future which will resolve with the result of this shared_future
    ///
    /// \param timeout When engaged, the returned future will resolve with \ref timed_out_error
    /// if this shared_future doesn't resolve before timeout is reached.
    ///
    /// This object must be in a valid state.
    future_type get_future(time_point timeout = time_point::max()) const {
        return _state->get_future(timeout);
    }

    /// \brief Returns true if the future is available (ready or failed)
    ///
    /// \note This object must be in a valid state.
    bool available() const noexcept {
        return _state->available();
    }

    /// \brief Returns true if the future is failed
    ///
    /// \note This object must be in a valid state.
    bool failed() const noexcept {
        return _state->failed();
    }

    /// \brief Equivalent to \ref get_future()
    operator future_type() const {
        return get_future();
    }

    /// \brief Returns true if the instance is in valid state
    bool valid() const {
        return bool(_state);
    }
};

/// \brief Like \ref promise except that its counterpart is \ref shared_future instead of \ref future
///
/// When the shared_promise is made ready, every waiter is also made ready.
///
/// Like the shared_future, the types in the parameter pack T must all be copy-constructible.
template <typename... T>
class shared_promise {
public:
    using shared_future_type = shared_future<T...>;
    using future_type = typename shared_future_type::future_type;
    using promise_type = typename shared_future_type::promise_type;
    using clock = typename shared_future_type::clock;
    using time_point = typename shared_future_type::time_point;
    using value_tuple_type = typename shared_future_type::value_tuple_type;
    using future_state_type = typename shared_future_type::future_state_type;
private:
    promise_type _promise;
    shared_future_type _shared_future;
    static constexpr bool copy_noexcept = future_state_type::copy_noexcept;
public:
    shared_promise(const shared_promise&) = delete;
    shared_promise(shared_promise&&) = default;
    shared_promise& operator=(shared_promise&&) = default;
    shared_promise() : _promise(), _shared_future(_promise.get_future()) {
    }

    /// \brief Gets new future associated with this promise.
    /// If the promise is not resolved before timeout the returned future will resolve with \ref timed_out_error.
    /// This instance doesn't have to be kept alive until the returned future resolves.
    future_type get_shared_future(time_point timeout = time_point::max()) const {
        return _shared_future.get_future(timeout);
    }

    /// \brief Sets the shared_promise's value (as tuple; by copying), same as normal promise
    void set_value(const value_tuple_type& result) noexcept(copy_noexcept) {
        _promise.set_value(result);
    }

    /// \brief Sets the shared_promise's value (as tuple; by moving), same as normal promise
    void set_value(value_tuple_type&& result) noexcept {
        _promise.set_value(std::move(result));
    }

    /// \brief Sets the shared_promise's value (variadic), same as normal promise
    template <typename... A>
    void set_value(A&&... a) noexcept {
        _promise.set_value(std::forward<A>(a)...);
    }

    /// \brief Marks the shared_promise as failed, same as normal promise
    void set_exception(std::exception_ptr ex) noexcept {
        _promise.set_exception(std::move(ex));
    }

    /// \brief Marks the shared_promise as failed, same as normal promise
    template<typename Exception>
    void set_exception(Exception&& e) noexcept {
        set_exception(make_exception_ptr(std::forward<Exception>(e)));
    }

    /// \brief Returns true if the underlying future is available (ready or failed)
    bool available() const noexcept {
        return _shared_future.available();
    }

    /// \brief Returns true if the underlying future is  failed
    bool failed() const noexcept {
        return _shared_future.failed();
    }
};

/// @}

}
<|endoftext|>