This commit is contained in:
Armin 2026-07-23 23:40:48 +02:00
commit d21bc831e1
178 changed files with 24136 additions and 0 deletions

237
External/Gin/gin_distortion.h vendored Normal file
View file

@ -0,0 +1,237 @@
/*
==============================================================================
This file is part of the GIN library.
Copyright (c) 2020 - Roland Rabien.
MIT License
Copyright (c) 2018 Chris Johnson
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.
==============================================================================
*/
#pragma once
#include <JuceHeader.h>
namespace gin
{
//==============================================================================
/** Distortion based on AirWindows plugins
*/
class AirWindowsDistortion
{
public:
AirWindowsDistortion()
{
reset();
}
void setSampleRate(double sr)
{
sampleRate = sr;
}
void reset()
{
A = 0.2f;
B = 0.0f;
C = 1.0f;
D = 1.0f;
iirSampleAL = 0.0f;
iirSampleBL = 0.0f;
iirSampleAR = 0.0f;
iirSampleBR = 0.0f;
fpFlip = true;
fpNShapeL = 0.0f;
fpNShapeR = 0.0f;
}
void setParams(float density, float highpass, float output, float mix)
{
A = density;
B = highpass;
C = output;
D = mix;
}
void process(float* l, float* r, int sampleFrames)
{
double overallscale = 1.0;
overallscale /= 44100.0;
overallscale *= sampleRate;
double density = (A * 5.0) - 1.0;
double iirAmount = pow(B, 3) / overallscale;
double output = C;
double wet = D;
double dry = 1.0 - wet;
double bridgerectifier;
double out = fabs(density);
density = density * fabs(density);
double count;
long double inputSampleL;
long double inputSampleR;
long double drySampleL;
long double drySampleR;
while (--sampleFrames >= 0)
{
inputSampleL = *l;
inputSampleR = *r;
if (inputSampleL < 1.2e-38 && -inputSampleL < 1.2e-38) {
static int noisesource = 0;
//this declares a variable before anything else is compiled. It won't keep assigning
//it to 0 for every sample, it's as if the declaration doesn't exist in this context,
//but it lets me add this denormalization fix in a single place rather than updating
//it in three different locations. The variable isn't thread-safe but this is only
//a random seed and we can share it with whatever.
noisesource = noisesource % 1700021; noisesource++;
int residue = noisesource * noisesource;
residue = residue % 170003; residue *= residue;
residue = residue % 17011; residue *= residue;
residue = residue % 1709; residue *= residue;
residue = residue % 173; residue *= residue;
residue = residue % 17;
double applyresidue = residue;
applyresidue *= 0.00000001;
applyresidue *= 0.00000001;
inputSampleL = applyresidue;
}
if (inputSampleR < 1.2e-38 && -inputSampleR < 1.2e-38) {
static int noisesource = 0;
noisesource = noisesource % 1700021; noisesource++;
int residue = noisesource * noisesource;
residue = residue % 170003; residue *= residue;
residue = residue % 17011; residue *= residue;
residue = residue % 1709; residue *= residue;
residue = residue % 173; residue *= residue;
residue = residue % 17;
double applyresidue = residue;
applyresidue *= 0.00000001;
applyresidue *= 0.00000001;
inputSampleR = applyresidue;
//this denormalization routine produces a white noise at -300 dB which the noise
//shaping will interact with to produce a bipolar output, but the noise is actually
//all positive. That should stop any variables from going denormal, and the routine
//only kicks in if digital black is input. As a final touch, if you save to 24-bit
//the silence will return to being digital black again.
}
drySampleL = inputSampleL;
drySampleR = inputSampleR;
if (fpFlip)
{
iirSampleAL = double((iirSampleAL * (1.0 - iirAmount)) + (inputSampleL * iirAmount));
inputSampleL -= iirSampleAL;
iirSampleAR = double((iirSampleAR * (1.0 - iirAmount)) + (inputSampleR * iirAmount));
inputSampleR -= iirSampleAR;
}
else
{
iirSampleBL = double((iirSampleBL * (1.0 - iirAmount)) + (inputSampleL * iirAmount));
inputSampleL -= iirSampleBL;
iirSampleBR = double((iirSampleBR * (1.0 - iirAmount)) + (inputSampleR * iirAmount));
inputSampleR -= iirSampleBR;
}
//highpass section
fpFlip = !fpFlip;
count = density;
while (count > 1.0)
{
bridgerectifier = double(fabs(inputSampleL) * 1.57079633);
if (bridgerectifier > 1.57079633) bridgerectifier = 1.57079633;
//max value for sine function
bridgerectifier = sin(bridgerectifier);
if (inputSampleL > 0.0) inputSampleL = bridgerectifier;
else inputSampleL = -bridgerectifier;
bridgerectifier = double(fabs(inputSampleR) * 1.57079633);
if (bridgerectifier > 1.57079633) bridgerectifier = 1.57079633;
//max value for sine function
bridgerectifier = sin(bridgerectifier);
if (inputSampleR > 0.0) inputSampleR = bridgerectifier;
else inputSampleR = -bridgerectifier;
count = count - 1.0;
}
//we have now accounted for any really high density settings.
while (out > 1.0) out = out - 1.0;
bridgerectifier = double(fabs(inputSampleL) * 1.57079633);
if (bridgerectifier > 1.57079633) bridgerectifier = 1.57079633;
//max value for sine function
if (density > 0) bridgerectifier = sin(bridgerectifier);
else bridgerectifier = 1 - cos(bridgerectifier);
//produce either boosted or starved version
if (inputSampleL > 0) inputSampleL = (inputSampleL * (1 - out)) + (bridgerectifier * out);
else inputSampleL = (inputSampleL * (1 - out)) - (bridgerectifier * out);
//blend according to density control
bridgerectifier = double(fabs(inputSampleR) * 1.57079633);
if (bridgerectifier > 1.57079633) bridgerectifier = 1.57079633;
//max value for sine function
if (density > 0) bridgerectifier = sin(bridgerectifier);
else bridgerectifier = 1 - cos(bridgerectifier);
//produce either boosted or starved version
if (inputSampleR > 0) inputSampleR = (inputSampleR * (1.0 - out)) + (bridgerectifier * out);
else inputSampleR = (inputSampleR * (1.0 - out)) - (bridgerectifier * out);
//blend according to density control
if (output < 1.0) {
inputSampleL *= output;
inputSampleR *= output;
}
if (wet < 1.0) {
inputSampleL = (drySampleL * dry) + (inputSampleL * wet);
inputSampleR = (drySampleR * dry) + (inputSampleR * wet);
}
//nice little output stage template: if we have another scale of floating point
//number, we really don't want to meaninglessly multiply that by 1.0.
//stereo 32 bit dither, made small and tidy.
int expon; frexpf((float)inputSampleL, &expon);
long double dither = (rand() / (RAND_MAX * 7.737125245533627e+25)) * pow(2, expon + 62);
inputSampleL += (dither - fpNShapeL); fpNShapeL = dither;
frexpf((float)inputSampleR, &expon);
dither = (rand() / (RAND_MAX * 7.737125245533627e+25)) * pow(2, expon + 62);
inputSampleR += (dither - fpNShapeR); fpNShapeR = dither;
//end 32 bit dither
*l = float(inputSampleL);
*r = float(inputSampleR);
l++;
r++;
}
}
private:
double sampleRate = 44100.0;
long double fpNShapeL, fpNShapeR;
double iirSampleAL, iirSampleBL, iirSampleAR, iirSampleBR;
bool fpFlip;
float A, B, C, D;
};
} // namespace gin

328
External/Gin/gin_simpleverb.cpp vendored Normal file
View file

@ -0,0 +1,328 @@
/*
==============================================================================
This file is part of the GIN library.
Copyright (c) 2019 - Roland Rabien.
==============================================================================
*/
#include "gin_simpleverb.h"
gin::SimpleVerb::SimpleVerb()
{
roomSizeFader = 0.5;
roomSize = 55;
preDelayFader = 0;
preDelayLength = 0;
preDelayPos = 0;
dampFader = 0.5;
damp = 0.25;
freqLPFader = 1;
freqHPFader = 0;
freqLP = 24000;
freqHP = 0;
b1LP = -std::exp(-2.0f * juce::MathConstants<float>::pi * freqLP / sampleRate); // 100Hz
a0LP = 1.0f + b1LP;
b1HP = -std::exp(-2.0f * juce::MathConstants<float>::pi * freqHP / sampleRate); // 100Hz
a0HP = 1.0f + b1HP;
dry = 1;
wet = 0.5;
setSampleRate(44100);
}
void gin::SimpleVerb::setSampleRate(float sr)
{
constexpr float roomMaxSize = 100.0f;
sampleRate = sr;
auto comb1MaxLength = static_cast<unsigned int>(C1 * roomMaxSize * sampleRate / 1000);
comb1.resize(comb1MaxLength);
auto comb2MaxLength = static_cast<unsigned int>(C2 * roomMaxSize * sampleRate / 1000);
comb2.resize(comb2MaxLength);
auto comb3MaxLength = static_cast<unsigned int>(C3 * roomMaxSize * sampleRate / 1000);
comb3.resize(comb3MaxLength);
auto comb4MaxLength = static_cast<unsigned int>(C4 * roomMaxSize * sampleRate / 1000);
comb4.resize(comb4MaxLength);
auto comb5MaxLength = static_cast<unsigned int>(C5 * roomMaxSize * sampleRate / 1000);
comb5.resize(comb5MaxLength);
auto comb6MaxLength = static_cast<unsigned int>(C6 * roomMaxSize * sampleRate / 1000);
comb6.resize(comb6MaxLength);
auto comb7MaxLength = static_cast<unsigned int>(C7 * roomMaxSize * sampleRate / 1000);
comb7.resize(comb7MaxLength);
auto comb8MaxLength = static_cast<unsigned int>(C8 * roomMaxSize * sampleRate / 1000);
comb8.resize(comb8MaxLength);
auto comb9MaxLength = static_cast<unsigned int>(C9 * roomMaxSize * sampleRate / 1000);
comb9.resize(comb9MaxLength);
auto comb10MaxLength = static_cast<unsigned int>(C10 * roomMaxSize * sampleRate / 1000);
comb10.resize(comb10MaxLength);
auto comb11MaxLength = static_cast<unsigned int>(C11 * roomMaxSize * sampleRate / 1000);
comb11.resize(comb11MaxLength);
auto comb12MaxLength = static_cast<unsigned int>(C12 * roomMaxSize * sampleRate / 1000);
comb12.resize(comb12MaxLength);
allpassL1Length = static_cast<unsigned int>(AL1 * sampleRate / 1000);
allpassL1.resize(allpassL1Length);
allpassL2Length = static_cast<unsigned int>((AL2 + SW) * sampleRate / 1000);
allpassL2.resize(allpassL2Length);
allpassL3Length = static_cast<unsigned int>(AL3 * sampleRate / 1000);
allpassL3.resize(allpassL3Length);
allpassR1Length = static_cast<unsigned int>((AR1 + SW) * sampleRate / 1000);
allpassR1.resize(allpassR1Length);
allpassR2Length = static_cast<unsigned int>(AR2 * sampleRate / 1000);
allpassR2.resize(allpassR2Length);
allpassR3Length = static_cast<unsigned int>((AR3 + SW) * sampleRate / 1000);
allpassR3.resize(allpassR3Length);
auto preDelayMaxLength = static_cast<unsigned int>(500 * sampleRate / 1000);
preDelay.resize(preDelayMaxLength);
flushPreDelay();
flushBuffers();
allpassL1Pos = allpassL2Pos = allpassL3Pos = allpassR1Pos = allpassR2Pos = allpassR3Pos = 0;
tmp1LP = tmp2LP = tmp1HP = tmp2HP = 0;
comb1Pos = comb2Pos = comb3Pos = comb4Pos = comb5Pos = comb6Pos = comb7Pos = comb8Pos = 0;
comb9Pos = comb10Pos = comb11Pos = comb12Pos = 0;
preDelayPos = 0;
}
void gin::SimpleVerb::flushPreDelay()
{
std::fill(preDelay.begin(), preDelay.end(), 0.0f);
}
void gin::SimpleVerb::flushBuffers()
{
std::fill(comb1.begin(), comb1.end(), 0.0f);
std::fill(comb2.begin(), comb2.end(), 0.0f);
std::fill(comb3.begin(), comb3.end(), 0.0f);
std::fill(comb4.begin(), comb4.end(), 0.0f);
std::fill(comb5.begin(), comb5.end(), 0.0f);
std::fill(comb6.begin(), comb6.end(), 0.0f);
std::fill(comb7.begin(), comb7.end(), 0.0f);
std::fill(comb8.begin(), comb8.end(), 0.0f);
std::fill(comb9.begin(), comb9.end(), 0.0f);
std::fill(comb10.begin(), comb10.end(), 0.0f);
std::fill(comb11.begin(), comb11.end(), 0.0f);
std::fill(comb12.begin(), comb12.end(), 0.0f);
std::fill(allpassL1.begin(), allpassL1.end(), 0.0f);
std::fill(allpassL2.begin(), allpassL2.end(), 0.0f);
std::fill(allpassL3.begin(), allpassL3.end(), 0.0f);
std::fill(allpassR1.begin(), allpassR1.end(), 0.0f);
std::fill(allpassR2.begin(), allpassR2.end(), 0.0f);
std::fill(allpassR3.begin(), allpassR3.end(), 0.0f);
}
void gin::SimpleVerb::setParameters(float roomIn, float dampIn, float preDelayIn, float lpFaderIn, float hpFaderIn, float wetIn, float dryIn)
{
if (!juce::approximatelyEqual(roomIn, roomSizeFader))
{
roomSizeFader = roomIn;
roomSize = 5 + roomSizeFader * roomSizeFader * 95;
comb1Length = static_cast<unsigned int>(C1 * roomSize * sampleRate / 1000);
comb1Pos = 0;
comb2Length = static_cast<unsigned int>(C2 * roomSize * sampleRate / 1000);
comb2Pos = 0;
comb3Length = static_cast<unsigned int>(C3 * roomSize * sampleRate / 1000);
comb3Pos = 0;
comb4Length = static_cast<unsigned int>(C4 * roomSize * sampleRate / 1000);
comb4Pos = 0;
comb5Length = static_cast<unsigned int>(C5 * roomSize * sampleRate / 1000);
comb5Pos = 0;
comb6Length = static_cast<unsigned int>(C6 * roomSize * sampleRate / 1000);
comb6Pos = 0;
comb7Length = static_cast<unsigned int>(C7 * roomSize * sampleRate / 1000);
comb7Pos = 0;
comb8Length = static_cast<unsigned int>(C8 * roomSize * sampleRate / 1000);
comb8Pos = 0;
comb9Length = static_cast<unsigned int>(C9 * roomSize * sampleRate / 1000);
comb9Pos = 0;
comb10Length = static_cast<unsigned int>(C10 * roomSize * sampleRate / 1000);
comb10Pos = 0;
comb11Length = static_cast<unsigned int>(C11 * roomSize * sampleRate / 1000);
comb11Pos = 0;
comb12Length = static_cast<unsigned int>(C12 * roomSize * sampleRate / 1000);
comb12Pos = 0;
flushBuffers();
}
if (!juce::approximatelyEqual(dampIn, dampFader))
{
dampFader = dampIn;
damp = std::min(1.0f - dampFader * dampFader, 0.95f);
}
if (!juce::approximatelyEqual(preDelayIn, preDelayFader))
{
preDelayFader = preDelayIn;
preDelayLength = static_cast<unsigned int>(preDelayFader * preDelayFader * 250 * sampleRate / 1000);
preDelayPos = 0;
flushPreDelay();
}
if (!juce::approximatelyEqual(lpFaderIn, freqLPFader))
{
freqLPFader = lpFaderIn;
freqLP = freqLPFader * freqLPFader * freqLPFader * 24000;
b1LP = -std::exp(-2.0f * juce::MathConstants<float>::pi * freqLP / sampleRate); // 100Hz
a0LP = 1.0f + b1LP;
}
if (!juce::approximatelyEqual(hpFaderIn, freqHPFader))
{
freqHPFader = hpFaderIn;
freqHP = freqHPFader * freqHPFader * freqHPFader * 24000;
b1HP = -std::exp(-2.0f * juce::MathConstants<float>::pi * freqHP / sampleRate); // 100Hz
a0HP = 1.0f + b1HP;
}
if (!juce::approximatelyEqual(dryIn, dryFader))
{
dryFader = dryIn;
dry = dryFader * 2;
}
if (!juce::approximatelyEqual(wetIn, wetFader))
{
wetFader = wetIn;
wet = wetFader * 2;
}
}
void gin::SimpleVerb::process(const float* in1, const float* in2, float* out1, float* out2, int numSamples)
{
int sampleFrames = numSamples;
while (--sampleFrames >= 0)
{
if (preDelayLength <= 1)
{
reverb = ((*in1) + (*in2)) / (1 + damp) + cDC_;
}
else
{
preDelay[preDelayPos] = ((*in1) + (*in2)) / (1 + damp) + cDC_;
if (++preDelayPos >= preDelayLength)
preDelayPos = 0;
reverb = preDelay[preDelayPos];
}
comb1[comb1Pos] = reverb * 0.49f + comb1[comb1Pos] * damp;
comb2[comb2Pos] = reverb * 0.76f + comb2[comb2Pos] * damp;
comb3[comb3Pos] = reverb * 1.00f + comb3[comb3Pos] * damp;
comb4[comb4Pos] = reverb * 0.91f + comb4[comb4Pos] * damp;
comb5[comb5Pos] = reverb * 0.79f + comb5[comb5Pos] * damp;
comb6[comb6Pos] = reverb * 0.71f + comb6[comb6Pos] * damp;
comb7[comb7Pos] = reverb * 0.59f + comb7[comb7Pos] * damp;
comb8[comb8Pos] = reverb * 0.51f + comb8[comb8Pos] * damp;
comb9[comb9Pos] = reverb * 0.42f + comb9[comb9Pos] * damp;
comb10[comb10Pos] = reverb * 0.38f + comb10[comb10Pos] * damp;
comb11[comb11Pos] = reverb * 0.35f + comb11[comb11Pos] * damp;
comb12[comb12Pos] = reverb * 0.30f + comb12[comb12Pos] * damp;
if (++comb1Pos >= comb1Length) comb1Pos = 0;
if (++comb2Pos >= comb2Length) comb2Pos = 0;
if (++comb3Pos >= comb3Length) comb3Pos = 0;
if (++comb4Pos >= comb4Length) comb4Pos = 0;
if (++comb5Pos >= comb5Length) comb5Pos = 0;
if (++comb6Pos >= comb6Length) comb6Pos = 0;
if (++comb7Pos >= comb7Length) comb7Pos = 0;
if (++comb8Pos >= comb8Length) comb8Pos = 0;
if (++comb9Pos >= comb9Length) comb9Pos = 0;
if (++comb10Pos >= comb10Length) comb10Pos = 0;
if (++comb11Pos >= comb11Length) comb11Pos = 0;
if (++comb12Pos >= comb12Length) comb12Pos = 0;
reverb = (comb1[comb1Pos]
+ comb2[comb2Pos]
+ comb3[comb3Pos]
+ comb4[comb4Pos]
+ comb5[comb5Pos]
+ comb6[comb6Pos]
+ comb7[comb7Pos]
+ comb8[comb8Pos]
+ comb9[comb9Pos]
+ comb10[comb10Pos]
+ comb11[comb11Pos]
+ comb12[comb12Pos]);
jassert(!std::isnan(reverb) && !std::isinf(reverb));
allpassL1[allpassL1Pos] = reverb + allpassL1[allpassL1Pos] * AP1FBQ;
left = (reverb - allpassL1[allpassL1Pos] * AP1FBQ);
jassert(!std::isnan(left) && !std::isinf(left));
if (++allpassL1Pos >= allpassL1Length)
allpassL1Pos = 0;
allpassL2[allpassL2Pos] = left + allpassL2[allpassL2Pos] * AP2FBQ;
left = (left - allpassL2[allpassL2Pos] * AP2FBQ);
jassert(!std::isnan(left) && !std::isinf(left));
if (++allpassL2Pos >= allpassL2Length)
allpassL2Pos = 0;
allpassL3[allpassL3Pos] = left + allpassL3[allpassL3Pos] * AP3FBQ;
left = (left - allpassL3[allpassL3Pos] * AP3FBQ);
jassert(!std::isnan(left) && !std::isinf(left));
if (++allpassL3Pos >= allpassL3Length)
allpassL3Pos = 0;
allpassR1[allpassR1Pos] = reverb + allpassR1[allpassR1Pos] * AP1FBQ;
right = (reverb - allpassR1[allpassR1Pos] * AP1FBQ);
jassert(!std::isnan(right) && !std::isinf(right));
if (++allpassR1Pos >= allpassR1Length)
allpassR1Pos = 0;
allpassR2[allpassR2Pos] = right + allpassR2[allpassR2Pos] * AP2FBQ;
right = (right - allpassR2[allpassR2Pos] * AP2FBQ);
jassert(!std::isnan(right) && !std::isinf(right));
if (++allpassR2Pos >= allpassR2Length)
allpassR2Pos = 0;
allpassR3[allpassR3Pos] = right + allpassR3[allpassR3Pos] * AP3FBQ;
right = (right - allpassR3[allpassR3Pos] * AP3FBQ);
jassert(!std::isnan(right) && !std::isinf(right));
if (++allpassR3Pos >= allpassR3Length)
allpassR3Pos = 0;
if (!juce::approximatelyEqual(freqHPFader, 0.0f))
{
left -= (tmp1HP = a0HP * left - b1HP * tmp1HP + cDC_) - cDC_;
right -= (tmp2HP = a0HP * right - b1HP * tmp2HP + cDC_) - cDC_;
jassert(!std::isnan(left) && !std::isinf(left));
jassert(!std::isnan(right) && !std::isinf(right));
}
if (!juce::approximatelyEqual(freqLPFader, 1.0f))
{
left = (tmp1LP = a0LP * left - b1LP * tmp1LP + cDC_) - cDC_;
right = (tmp2LP = a0LP * right - b1LP * tmp2LP + cDC_) - cDC_;
jassert(!std::isnan(left) && !std::isinf(left));
jassert(!std::isnan(right) && !std::isinf(right));
}
(*out1++) = (*in1++) * dry + left * wet;
(*out2++) = (*in2++) * dry + right * wet;
}
}

134
External/Gin/gin_simpleverb.h vendored Normal file
View file

@ -0,0 +1,134 @@
/*
==============================================================================
This file is part of the GIN library.
Copyright (c) 2019 - Roland Rabien.
==============================================================================
*/
#pragma once
#include <JuceHeader.h>
namespace gin
{
/** Simple Reverb
Copyright (c) 2006-2008 and 2012, Michael "LOSER" Gruhn
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY, FITNESS AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
*/
class SimpleVerb
{
public:
SimpleVerb();
void setSampleRate(float sr);
void process(const float* in1, const float* in2, float* out1, float* out2, int numSamples);
void setParameters(float roomIn, float dampIn, float preDelayIn, float lpFaderIn, float hpFaderIn, float wetIn, float dryIn);
private:
void flushPreDelay();
void flushBuffers();
unsigned int preDelayPos, preDelayLength;
std::vector<float> preDelay;
float preDelayFader;
unsigned int comb1Pos, comb1Length;
std::vector<float> comb1;
unsigned int comb2Pos, comb2Length;
std::vector<float> comb2;
unsigned int comb3Pos, comb3Length;
std::vector<float> comb3;
unsigned int comb4Pos, comb4Length;
std::vector<float> comb4;
unsigned int comb5Pos, comb5Length;
std::vector<float> comb5;
unsigned int comb6Pos, comb6Length;
std::vector<float> comb6;
unsigned int comb7Pos, comb7Length;
std::vector<float> comb7;
unsigned int comb8Pos, comb8Length;
std::vector<float> comb8;
unsigned int comb9Pos, comb9Length;
std::vector<float> comb9;
unsigned int comb10Pos, comb10Length;
std::vector<float> comb10;
unsigned int comb11Pos, comb11Length;
std::vector<float> comb11;
unsigned int comb12Pos, comb12Length;
std::vector<float> comb12;
unsigned int allpassL1Pos, allpassL1Length;
std::vector<float> allpassL1;
unsigned int allpassL2Pos, allpassL2Length;
std::vector<float> allpassL2;
unsigned int allpassL3Pos, allpassL3Length;
std::vector<float> allpassL3;
unsigned int allpassR1Pos, allpassR1Length;
std::vector<float> allpassR1;
unsigned int allpassR2Pos, allpassR2Length;
std::vector<float> allpassR2;
unsigned int allpassR3Pos, allpassR3Length;
std::vector<float> allpassR3;
float reverb, damp, dry, wet, left, right;
float roomSize = -1.0f;
float roomSizeFader = -1.0f;
float dampFader = -1.0f;
float dryFader = -1.0f;
float wetFader = -1.0f;
float sampleRate = 44100.0f;
float freqLP, freqLPFader;
float freqHP, freqHPFader;
float a0LP, b1LP, tmp1LP, tmp2LP;
float a0HP, b1HP, tmp1HP, tmp2HP;
static constexpr float cDC_ = 1e-30f;
static constexpr float C1 = 1.00f;
static constexpr float C2 = 1.09f;
static constexpr float C3 = 1.16f;
static constexpr float C4 = 1.23f;
static constexpr float C5 = 1.32f;
static constexpr float C6 = 1.41f;
static constexpr float C7 = 1.45f;
static constexpr float C8 = 1.56f;
static constexpr float C9 = 1.66f;
static constexpr float C10 = 1.71f;
static constexpr float C11 = 1.80f;
static constexpr float C12 = 1.90f;
static constexpr float AL1 = 1.0f;
static constexpr float AL2 = 2.5f;
static constexpr float AL3 = 5.0f;
static constexpr float AR1 = 1.0f;
static constexpr float AR2 = 2.5f;
static constexpr float AR3 = 5.0f;
static constexpr float SW = 1.0f;
static constexpr float AP1FBQ = 0.6f;
static constexpr float AP2FBQ = 0.6f;
static constexpr float AP3FBQ = 0.6f;
};
} // namespace gin

958
External/MTS/libMTSClient.cpp vendored Normal file
View file

@ -0,0 +1,958 @@
/*
Copyright (C) 2021 by ODDSound Ltd. info@oddsound.com
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
*/
#include "libMTSClient.h"
#include <math.h>
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) || defined(__TOS_WIN__) || defined(_MSC_VER)
#define MTS_ESP_WIN
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
typedef HRESULT (WINAPI* SHGetKnownFolderPathFunc) (const GUID*, DWORD, HANDLE, PWSTR*);
typedef void (WINAPI* CoTaskMemFreeFunc) (LPVOID);
#else
#include <dlfcn.h>
#endif
const static int libMTSVersion = 0x00010003;
const static double ln2 = 0.693147180559945309417;
const static double ratioToSemitones = 17.31234049066756088832; // 12.0 / log(2.0)
typedef void (*mts_void__void)(void);
typedef bool (*mts_bool__void)(void);
typedef int (*mts_int__void)(void);
typedef bool (*mts_bool__char_char)(char, char);
typedef const double *(*mts_pConstDouble__void)(void);
typedef const double *(*mts_pConstDouble__char)(char);
typedef bool (*mts_bool__char)(char);
typedef const char *(*mts_pConstChar__void)(void);
typedef double (*mts_double__void)(void);
typedef char (*mts_char__void)(void);
struct mtsclientglobal
{
mtsclientglobal()
: RegisterClient(0)
, DeregisterClient(0)
, HasMaster(0)
, GetVersionNumber(0)
, ShouldFilterNote(0)
, ShouldFilterNoteMultiChannel(0)
, GetTuning(0)
, GetMultiChannelTuning(0)
, UseMultiChannelTuning(0)
, GetScaleName(0)
, GetPeriodRatio(0)
, GetMapSize(0)
, GetMapStartKey(0)
, GetRefKey(0)
, esp_retuning(0)
, handle(0)
{
for (int i = 0; i < 128; i++)
iet[i] = 1. / (440.0 * pow(2.0, (i - 69.0) / 12.0));
load_lib();
if (GetTuning)
esp_retuning = GetTuning();
for (int i = 0; i < 16; i++)
multi_channel_esp_retuning[i] = GetMultiChannelTuning ? GetMultiChannelTuning(static_cast<char>(i)) : 0;
}
inline bool isOnline() const {return esp_retuning && HasMaster && HasMaster();}
// interface to lib
mts_void__void RegisterClient;
mts_void__void DeregisterClient;
mts_bool__void HasMaster;
mts_int__void GetVersionNumber;
mts_bool__char_char ShouldFilterNote;
mts_bool__char_char ShouldFilterNoteMultiChannel;
mts_pConstDouble__void GetTuning;
mts_pConstDouble__char GetMultiChannelTuning;
mts_bool__char UseMultiChannelTuning;
mts_pConstChar__void GetScaleName;
mts_double__void GetPeriodRatio;
mts_char__void GetMapSize;
mts_char__void GetMapStartKey;
mts_char__void GetRefKey;
// tuning tables
double iet[128];
const double *esp_retuning;
const double *multi_channel_esp_retuning[16];
#ifdef MTS_ESP_WIN
void load_lib()
{
SHGetKnownFolderPathFunc SHGetKnownFolderPath = 0;
CoTaskMemFreeFunc CoTaskMemFree = 0;
HMODULE shell32Module = GetModuleHandleW(L"Shell32.dll");
HMODULE ole32Module = GetModuleHandleW(L"Ole32.dll");
if (shell32Module)
SHGetKnownFolderPath = (SHGetKnownFolderPathFunc)GetProcAddress(shell32Module, "SHGetKnownFolderPath");
if (ole32Module)
CoTaskMemFree = (CoTaskMemFreeFunc)GetProcAddress(ole32Module, "CoTaskMemFree");
if (SHGetKnownFolderPath && CoTaskMemFree)
{
const GUID FOLDERID_ProgramFilesCommonGUID = {0xF7F1ED05, 0x9F6D, 0x47A2, 0xAA, 0xAE, 0x29, 0xD3, 0x17, 0xC6, 0xF0, 0x66};
PWSTR cf = NULL;
if (SHGetKnownFolderPath(&FOLDERID_ProgramFilesCommonGUID, 0, 0, &cf) >= 0)
{
WCHAR buffer[MAX_PATH];
buffer[0] = L'\0';
if (cf)
wcsncpy(buffer, cf, MAX_PATH);
CoTaskMemFree(cf);
buffer[MAX_PATH - 1] = L'\0';
const WCHAR *libpath = L"\\MTS-ESP\\LIBMTS.dll";
DWORD cfLen = wcslen(buffer);
wcsncat(buffer, libpath, MAX_PATH - cfLen - 1);
handle = LoadLibraryW(buffer);
if (!handle)
return;
}
else
{
CoTaskMemFree(cf);
return;
}
}
else
{
return;
}
RegisterClient = (mts_void__void) GetProcAddress(handle, "MTS_RegisterClient");
DeregisterClient = (mts_void__void) GetProcAddress(handle, "MTS_DeregisterClient");
HasMaster = (mts_bool__void) GetProcAddress(handle, "MTS_HasMaster");
GetVersionNumber = (mts_int__void) GetProcAddress(handle, "MTS_GetVersionNumber");
ShouldFilterNote = (mts_bool__char_char) GetProcAddress(handle, "MTS_ShouldFilterNote");
ShouldFilterNoteMultiChannel = (mts_bool__char_char) GetProcAddress(handle, "MTS_ShouldFilterNoteMultiChannel");
GetTuning = (mts_pConstDouble__void) GetProcAddress(handle, "MTS_GetTuningTable");
GetMultiChannelTuning = (mts_pConstDouble__char) GetProcAddress(handle, "MTS_GetMultiChannelTuningTable");
UseMultiChannelTuning = (mts_bool__char) GetProcAddress(handle, "MTS_UseMultiChannelTuning");
GetScaleName = (mts_pConstChar__void) GetProcAddress(handle, "MTS_GetScaleName");
GetPeriodRatio = (mts_double__void) GetProcAddress(handle, "MTS_GetPeriodRatio");
GetMapSize = (mts_char__void) GetProcAddress(handle, "MTS_GetMapSize");
GetMapStartKey = (mts_char__void) GetProcAddress(handle, "MTS_GetMapStartKey");
GetRefKey = (mts_char__void) GetProcAddress(handle, "MTS_GetRefKey");
}
~mtsclientglobal()
{
if (handle)
FreeLibrary(handle);
}
HINSTANCE handle;
#else
void load_lib()
{
if (!(handle = dlopen("/Library/Application Support/MTS-ESP/libMTS.dylib", RTLD_NOW)) &&
!(handle = dlopen("/usr/local/lib/libMTS.so", RTLD_NOW)))
{
return;
}
RegisterClient = (mts_void__void) dlsym(handle, "MTS_RegisterClient");
DeregisterClient = (mts_void__void) dlsym(handle, "MTS_DeregisterClient");
HasMaster = (mts_bool__void) dlsym(handle, "MTS_HasMaster");
GetVersionNumber = (mts_int__void) dlsym(handle, "MTS_GetVersionNumber");
ShouldFilterNote = (mts_bool__char_char) dlsym(handle, "MTS_ShouldFilterNote");
ShouldFilterNoteMultiChannel = (mts_bool__char_char) dlsym(handle, "MTS_ShouldFilterNoteMultiChannel");
GetTuning = (mts_pConstDouble__void) dlsym(handle, "MTS_GetTuningTable");
GetMultiChannelTuning = (mts_pConstDouble__char) dlsym(handle, "MTS_GetMultiChannelTuningTable");
UseMultiChannelTuning = (mts_bool__char) dlsym(handle, "MTS_UseMultiChannelTuning");
GetScaleName = (mts_pConstChar__void) dlsym(handle, "MTS_GetScaleName");
GetPeriodRatio = (mts_double__void) dlsym(handle, "MTS_GetPeriodRatio");
GetMapSize = (mts_char__void) dlsym(handle, "MTS_GetMapSize");
GetMapStartKey = (mts_char__void) dlsym(handle, "MTS_GetMapStartKey");
GetRefKey = (mts_char__void) dlsym(handle, "MTS_GetRefKey");
}
~mtsclientglobal()
{
if (handle)
dlclose(handle);
}
void *handle;
#endif
};
static mtsclientglobal global;
struct MTSClient
{
struct Tuning
{
enum {eRatioValid = 1, eSemitonesValid = 1 << 1};
int flags;
double freq; // always valid
double ratio;
double semitones;
};
MTSClient()
: tuningName("12-TET")
, periodRatioLocal(2.0)
, periodSemitones(12.0)
, mapSizeLocal(static_cast<char>(-1))
, mapStartKeyLocal(static_cast<char>(-1))
, supportsNoteFiltering(false)
, supportsMultiChannelNoteFiltering(false)
, supportsMultiChannelTuning(false)
, freqRequestReceived(false)
, receivedMTSSysEx(false)
{
for (int i = 0; i < 128; i++)
{
localFreqs[i] = 440.0 * pow(2.0, (i - 69.0) / 12.0);
localTunings[i].flags = 0;
localTunings[i].freq = localFreqs[i];
globalTunings[i].flags = 0;
globalTunings[i].freq = localFreqs[i];
}
for (int i = 0; i < 16; i++)
{
for (int j = 0; j < 128; j++)
{
globalMultichannelTunings[i][j].flags = 0;
globalMultichannelTunings[i][j].freq = localFreqs[i];
}
}
if (global.RegisterClient)
global.RegisterClient();
}
~MTSClient()
{
if (global.DeregisterClient)
global.DeregisterClient();
}
inline bool hasMaster() {return global.isOnline();}
inline bool shouldUpdateLibrary() {return global.GetVersionNumber ? (global.GetVersionNumber() < libMTSVersion) : false;}
inline double freq(char midinote, char midichannel)
{
int note = midinote & 127;
int channel = midichannel & 15;
freqRequestReceived = true;
supportsMultiChannelTuning = !(midichannel & ~15);
if (!global.isOnline())
return localTunings[note].freq;
if ((!supportsNoteFiltering || supportsMultiChannelNoteFiltering) &&
supportsMultiChannelTuning &&
global.UseMultiChannelTuning &&
global.UseMultiChannelTuning(midichannel) &&
global.multi_channel_esp_retuning[channel])
{
globalMultichannelTunings[channel][note].freq = global.multi_channel_esp_retuning[channel][note];
globalMultichannelTunings[channel][note].flags = 0;
return globalMultichannelTunings[channel][note].freq;
}
globalTunings[note].freq = global.esp_retuning[note];
globalTunings[note].flags = 0;
return globalTunings[note].freq;
}
inline double ratio(char midinote, char midichannel)
{
int note = midinote & 127;
int channel = midichannel & 15;
freqRequestReceived = true;
supportsMultiChannelTuning = !(midichannel & ~15);
if (!global.isOnline())
{
if (!receivedMTSSysEx)
return 1.0;
if (localTunings[note].flags & Tuning::eRatioValid)
return localTunings[note].ratio;
localTunings[note].ratio = localTunings[note].freq * global.iet[note];
localTunings[note].flags |= Tuning::eRatioValid;
return localTunings[note].ratio;
}
if ((!supportsNoteFiltering || supportsMultiChannelNoteFiltering) &&
supportsMultiChannelTuning &&
global.UseMultiChannelTuning &&
global.UseMultiChannelTuning(midichannel) &&
global.multi_channel_esp_retuning[channel])
{
double freq = global.multi_channel_esp_retuning[channel][note];
if (globalMultichannelTunings[channel][note].freq == freq &&
(globalMultichannelTunings[channel][note].flags & Tuning::eRatioValid))
{
return globalMultichannelTunings[channel][note].ratio;
}
globalMultichannelTunings[channel][note].freq = global.multi_channel_esp_retuning[channel][note];
globalMultichannelTunings[channel][note].ratio = globalMultichannelTunings[channel][note].freq * global.iet[note];
globalMultichannelTunings[channel][note].flags = Tuning::eRatioValid;
return globalMultichannelTunings[channel][note].ratio;
}
double freq = global.esp_retuning[note];
if (globalTunings[note].freq == freq &&
(globalTunings[note].flags & Tuning::eRatioValid))
{
return globalTunings[note].ratio;
}
globalTunings[note].freq = global.esp_retuning[note];
globalTunings[note].ratio = globalTunings[note].freq * global.iet[note];
globalTunings[note].flags = Tuning::eRatioValid;
return globalTunings[note].ratio;
}
inline double semitones(char midinote, char midichannel)
{
int note = midinote & 127;
int channel = midichannel & 15;
freqRequestReceived = true;
supportsMultiChannelTuning = !(midichannel & ~15);
if (!global.isOnline())
{
if (!receivedMTSSysEx)
return 0.0;
if (localTunings[note].flags & Tuning::eSemitonesValid)
return localTunings[note].semitones;
if (localTunings[note].flags & Tuning::eRatioValid)
{
localTunings[note].semitones = ratioToSemitones * log(localTunings[note].ratio);
localTunings[note].flags |= Tuning::eSemitonesValid;
return localTunings[note].semitones;
}
localTunings[note].ratio = localTunings[note].freq * global.iet[note];
localTunings[note].semitones = ratioToSemitones * log(localTunings[note].ratio);
localTunings[note].flags |= Tuning::eRatioValid | Tuning::eSemitonesValid;
return localTunings[note].semitones;
}
if ((!supportsNoteFiltering || supportsMultiChannelNoteFiltering) &&
supportsMultiChannelTuning &&
global.UseMultiChannelTuning &&
global.UseMultiChannelTuning(midichannel) &&
global.multi_channel_esp_retuning[channel])
{
double freq = global.multi_channel_esp_retuning[channel][note];
if (globalMultichannelTunings[channel][note].freq == freq)
{
if (globalMultichannelTunings[channel][note].flags & Tuning::eSemitonesValid)
return globalMultichannelTunings[channel][note].semitones;
if (globalMultichannelTunings[channel][note].flags & Tuning::eRatioValid)
{
globalMultichannelTunings[channel][note].semitones = ratioToSemitones * log(globalMultichannelTunings[channel][note].ratio);
globalMultichannelTunings[channel][note].flags |= Tuning::eSemitonesValid;
return globalMultichannelTunings[channel][note].semitones;
}
}
globalMultichannelTunings[channel][note].freq = freq;
globalMultichannelTunings[channel][note].ratio = freq * global.iet[note];
globalMultichannelTunings[channel][note].semitones = ratioToSemitones * log(globalMultichannelTunings[channel][note].ratio);
globalMultichannelTunings[channel][note].flags = Tuning::eRatioValid | Tuning::eSemitonesValid;
return globalMultichannelTunings[channel][note].semitones;
}
double freq = global.esp_retuning[note];
if (globalTunings[note].freq == freq)
{
if (globalTunings[note].flags & Tuning::eSemitonesValid)
return globalTunings[note].semitones;
if (globalTunings[note].flags & Tuning::eRatioValid)
{
globalTunings[note].semitones = ratioToSemitones * log(globalTunings[note].ratio);
globalTunings[note].flags |= Tuning::eSemitonesValid;
return globalTunings[note].semitones;
}
}
globalTunings[note].freq = freq;
globalTunings[note].ratio = freq * global.iet[note];
globalTunings[note].semitones = ratioToSemitones * log(globalTunings[note].ratio);
globalTunings[note].flags = Tuning::eRatioValid | Tuning::eSemitonesValid;
return globalTunings[note].semitones;
}
inline bool shouldFilterNote(char midinote, char midichannel)
{
supportsNoteFiltering = true;
supportsMultiChannelNoteFiltering = !(midichannel & ~15);
if (!freqRequestReceived)
supportsMultiChannelTuning = supportsMultiChannelNoteFiltering; // assume it supports multi channel tuning until a request is received for a frequency and can verify
if (!global.isOnline())
return false;
if (supportsMultiChannelNoteFiltering &&
supportsMultiChannelTuning &&
global.UseMultiChannelTuning &&
global.UseMultiChannelTuning(midichannel))
{
return global.ShouldFilterNoteMultiChannel ? global.ShouldFilterNoteMultiChannel(midinote & 127, midichannel) : false;
}
return global.ShouldFilterNote ? global.ShouldFilterNote(midinote & 127, midichannel) : false;
}
inline char freqToNote(double freq, char midichannel)
{
bool online = global.isOnline();
bool multiChannel = false;
const double *freqs = online ? global.esp_retuning : localFreqs;
if (online &&
!(midichannel & ~15) &&
global.UseMultiChannelTuning &&
global.UseMultiChannelTuning(midichannel) &&
global.multi_channel_esp_retuning[midichannel & 15])
{
freqs = global.multi_channel_esp_retuning[midichannel & 15];
multiChannel = true;
}
int iLower = 0;
int iUpper = 0;
double dLower = 0.0;
double dUpper = 0.0;
for (int i = 0; i < 128; i++)
{
if (online)
{
if (multiChannel &&
global.ShouldFilterNoteMultiChannel &&
global.ShouldFilterNoteMultiChannel(static_cast<char>(i), midichannel))
{
continue;
}
if (!multiChannel &&
global.ShouldFilterNote &&
global.ShouldFilterNote(static_cast<char>(i), midichannel))
{
continue;
}
}
double d = freqs[i] - freq;
if (d == 0.0)
return static_cast<char>(i);
if (d < 0.0)
{
if (dLower == 0.0 || d > dLower)
{
dLower=d;
iLower=i;
}
}
else if (dUpper == 0.0 || d < dUpper)
{
dUpper = d;
iUpper = i;
}
}
if (dLower == 0.0)
return static_cast<char>(iUpper);
if (dUpper == 0.0 || iLower == iUpper)
return static_cast<char>(iLower);
double fmid = freqs[iLower] * pow(2.0, 0.5 * (log(freqs[iUpper] / freqs[iLower]) / ln2));
return freq < fmid ? static_cast<char>(iLower) : static_cast<char>(iUpper);
}
inline char freqToNote(double freq, char *midichannel)
{
if (!midichannel)
return freqToNote(freq, static_cast<char>(-1));
if (global.isOnline() && global.UseMultiChannelTuning)
{
int channelsInUse[16];
int nMultiChannels = 0;
for (int i = 0; i < 16; i++)
if (global.UseMultiChannelTuning(i) && global.multi_channel_esp_retuning[i])
channelsInUse[nMultiChannels++] = i;
if (nMultiChannels > 0)
{
const int nFreqs = 128 * nMultiChannels;
int iLower = 0;
int iUpper = 0;
int channel = 0;
int note = 0;
double dLower = 0.0;
double dUpper = 0.0;
for (int i = 0; i < nFreqs; i++)
{
channel = channelsInUse[i >> 7];
note = i & 127;
if (global.ShouldFilterNoteMultiChannel &&
global.ShouldFilterNoteMultiChannel(static_cast<char>(note), static_cast<char>(channel)))
{
continue;
}
double d = global.multi_channel_esp_retuning[channel][note] - freq;
if (d == 0.0)
{
*midichannel = static_cast<char>(channel);
return static_cast<char>(note);
}
if (d < 0.0)
{
if (dLower == 0.0 || d > dLower)
{
dLower = d;
iLower = i;
}
}
else if (dUpper == 0.0 || d < dUpper)
{
dUpper = d;
iUpper = i;
}
}
if (dLower==0.0)
{
*midichannel = static_cast<char>(channelsInUse[iUpper >> 7]);
return static_cast<char>(iUpper & 127);
}
if (dUpper == 0.0 || iLower == iUpper)
{
*midichannel = static_cast<char>(channelsInUse[iLower >> 7]);
return static_cast<char>(iLower & 127);
}
double fLower = global.multi_channel_esp_retuning[channelsInUse[iLower >> 7]][iLower & 127];
double fUpper = global.multi_channel_esp_retuning[channelsInUse[iUpper >> 7]][iUpper & 127];
double fmid = fLower * pow(2.0, 0.5 * (log(fUpper / fLower) / ln2));
if (freq < fmid)
{
*midichannel = static_cast<char>(channelsInUse[iLower >> 7]);
return static_cast<char>(iLower & 127);
}
*midichannel = static_cast<char>(channelsInUse[iUpper >> 7]);
return static_cast<char>(iUpper & 127);
}
}
*midichannel = static_cast<char>(0);
return freqToNote(freq, static_cast<char>(0));
}
inline void parseMIDIData(const unsigned char *buffer, int len)
{
int sysex_ctr = 0;
int sysex_value = 0;
int note = 0;
int numTunings = 0;
/*int bank = -1, prog = 0, checksum = 0, deviceID = 0; short int channelBitmap = 0; bool realtime = false;*/ // unused for now
eSysexState state = eIgnoring;
eMTSFormat format = eBulk;
for (int i = 0; i < len; i++)
{
unsigned char b = buffer[i];
if (b == 0xF7)
{
state = eIgnoring;
continue;
}
if (b > 0x7F && b != 0xF0)
continue;
switch (state)
{
case eIgnoring:
if (b == 0xF0)
state = eMatchingSysex;
break;
case eMatchingSysex:
sysex_ctr = 0;
if (b == 0x7E)
state = eSysexValid;
else if (b == 0x7F)
{
/*realtime = true;*/
state = eSysexValid;
}
else
{
state = eIgnoring;
}
break;
case eSysexValid:
switch (sysex_ctr++) // handle device ID
{
case 0:
/*deviceID = b;*/
break;
case 1:
if (b == 0x08)
state = eMatchingMTS;
break;
default: // it's not an MTS message
state = eIgnoring;
break;
}
break;
case eMatchingMTS:
sysex_ctr = 0;
switch (b)
{
case 0:
format = eRequest;
state = eMatchingProg;
break;
case 1:
format = eBulk;
state = eMatchingProg;
break;
case 2:
format = eSingle;
state = eMatchingProg;
break;
case 3:
format = eRequest;
state = eMatchingBank;
break;
case 4:
format = eBulk;
state = eMatchingBank;
break;
case 5:
format = eScaleOctOneByte;
state = eMatchingBank;
break;
case 6:
format = eScaleOctTwoByte;
state = eMatchingBank;
break;
case 7:
format = eSingle;
state = eMatchingBank;
break;
case 8:
format = eScaleOctOneByteExt;
state = eMatchingChannel;
break;
case 9:
format = eScaleOctTwoByteExt;
state = eMatchingChannel;
break;
default: // it's not a valid MTS format
state = eIgnoring;
break;
}
break;
case eMatchingBank:
/*bank = b;*/
state = eMatchingProg;
break;
case eMatchingProg:
/*prog = b;*/
if (format == eSingle)
{
state = eNumTunings;
}
else
{
state = eTuningName;
tuningName[0] = '\0';
}
break;
case eTuningName:
tuningName[sysex_ctr] = static_cast<char>(b);
if (++sysex_ctr >= 16)
{
tuningName[16] = '\0';
sysex_ctr = 0;
state = eTuningData;
}
break;
case eNumTunings:
numTunings = b;
sysex_ctr = 0;
state = eTuningData;
break;
case eMatchingChannel:
switch (sysex_ctr++)
{
case 0:
/*for (int j = 14; j < 16; j++) channelBitmap |= (1 << j);*/
break;
case 1:
/*for (int j = 7; j < 14; j++) channelBitmap |= (1 << j);*/
break;
case 2:
/*for (int j = 0; j < 7; j++) channelBitmap |= (1 << j);*/
sysex_ctr = 0;
state = eTuningData;
break;
}
break;
case eTuningData:
switch (format)
{
case eBulk:
sysex_value = (sysex_value << 7) | b;
sysex_ctr++;
if ((sysex_ctr & 3) == 3)
{
if (!(note == 0x7F && sysex_value == 16383))
updateTuning(note, (sysex_value >> 14) & 127, (sysex_value & 16383) / 16383.0);
sysex_value = 0;
sysex_ctr++;
if (++note >= 128)
state = eCheckSum;
}
break;
case eSingle:
sysex_value = (sysex_value << 7) | b;
sysex_ctr++;
if (!(sysex_ctr & 3))
{
if (!(note == 0x7F && sysex_value == 16383))
updateTuning((sysex_value >> 21) & 127, (sysex_value >> 14) & 127, (sysex_value & 16383) / 16383.0);
sysex_value = 0;
if (++note >= numTunings)
state = eIgnoring;
}
break;
case eScaleOctOneByte:
case eScaleOctOneByteExt:
for (int j = sysex_ctr; j < 128; j += 12)
updateTuning(j, j, (static_cast<double>(b) - 64.0) * 0.01);
if (++sysex_ctr >= 12)
state = format == eScaleOctOneByte ? eCheckSum : eIgnoring;
break;
case eScaleOctTwoByte:
case eScaleOctTwoByteExt:
sysex_value = (sysex_value << 7) | b;
sysex_ctr++;
if (!(sysex_ctr & 1))
{
double detune = (static_cast<double>(sysex_value & 16383) - 8192.0) / (sysex_value > 8192 ? 8191.0 : 8192.0);
for (int j = note; j < 128; j += 12)
updateTuning(j, j, detune);
if (++note >= 12)
state = format == eScaleOctTwoByte ? eCheckSum : eIgnoring;
}
break;
default:
state = eIgnoring;
break;
}
break;
case eCheckSum:
/*checksum = b;*/
state = eIgnoring;
break;
}
}
if (format == eScaleOctOneByte || format == eScaleOctTwoByte || format == eScaleOctOneByteExt || format == eScaleOctTwoByteExt)
{
mapSizeLocal = static_cast<char>(12);
mapStartKeyLocal = static_cast<char>(60);
}
else
{
mapSizeLocal = static_cast<char>(-1);
mapStartKeyLocal = static_cast<char>(-1);
}
}
inline void updateTuning(int note, int retuneNote, double detune)
{
if (note < 0 || note > 127 || retuneNote < 0 || retuneNote > 127)
return;
receivedMTSSysEx = true;
localFreqs[note] = 440.0 * pow(2.0, ((retuneNote + detune) - 69.0) / 12.0);
if (localFreqs[note] != localTunings[note].freq)
{
localTunings[note].freq = localFreqs[note];
localTunings[note].flags = 0;
}
}
inline bool hasReceivedMTSSysEx() {return receivedMTSSysEx;}
const char *getScaleName() {return (global.isOnline() && global.GetScaleName) ? global.GetScaleName() : tuningName;}
double getPeriodRatio() {return (global.isOnline() && global.GetPeriodRatio) ? global.GetPeriodRatio() : 2.0;}
double getPeriodSemitones()
{
double periodRatio = getPeriodRatio();
if (periodRatio != periodRatioLocal)
{
periodSemitones = ratioToSemitones * log(periodRatio);
periodRatioLocal = periodRatio;
}
return periodSemitones;
}
char getMapSize() {return (global.isOnline() && global.GetMapSize) ? global.GetMapSize() : mapSizeLocal;}
char getMapStartKey() {return (global.isOnline() && global.GetMapStartKey) ? global.GetMapStartKey() : mapStartKeyLocal;}
char getRefKey() {return (global.isOnline() && global.GetRefKey) ? global.GetRefKey() : static_cast<char>(-1);}
enum eSysexState {eIgnoring = 0, eMatchingSysex, eSysexValid, eMatchingMTS, eMatchingBank, eMatchingProg, eMatchingChannel, eTuningName, eNumTunings, eTuningData, eCheckSum};
enum eMTSFormat {eRequest = 0, eBulk, eSingle, eScaleOctOneByte, eScaleOctTwoByte, eScaleOctOneByteExt, eScaleOctTwoByteExt};
double localFreqs[128];
Tuning localTunings[128];
Tuning globalTunings[128];
Tuning globalMultichannelTunings[16][128];
char tuningName[17];
double periodRatioLocal;
double periodSemitones;
char mapSizeLocal;
char mapStartKeyLocal;
bool supportsNoteFiltering;
bool supportsMultiChannelNoteFiltering;
bool supportsMultiChannelTuning;
bool freqRequestReceived;
bool receivedMTSSysEx;
};
static char freqToNoteET(double freq)
{
static double freqs[128];
static bool init = false;
if (!init)
{
for (int i = 0; i < 128; i++)
freqs[i] = 440.0 * pow(2.0, (i - 69.0) / 12.0);
init = true;
}
if (freq <= freqs[0])
return 0;
if (freq >= freqs[127])
return 127;
int mid = 0;
int n = -1;
int n2 = -1;
for (int first = 0, last=127;
freq != freqs[(mid = first + (last - first) / 2)];
(freq < freqs[mid]) ? last = mid - 1 : first = mid + 1)
{
if (first > last)
{
if (!mid)
{
n = mid;
break;
}
if (mid > 127)
mid = 127;
n = mid - ((freq - freqs[mid - 1]) < (freqs[mid] - freq));
break;
}
}
if (n == -1)
{
if (freq == freqs[mid])
n = mid;
else
return 60;
}
if (!n)
n2 = 1;
else if (n == 127)
n2 = 126;
else
n2 = n + (fabs(freqs[n - 1] - freq) < fabs(freqs[n + 1] - freq) ? -1 : 1);
if (n2 < n)
{
int t = n;
n = n2;
n2 = t;
}
double fmid = freqs[n] * pow(2.0, 0.5 * (log(freqs[n2] / freqs[n]) / ln2));
return freq < fmid ? static_cast<char>(n) : static_cast<char>(n2);
}
// exported functions:
MTSClient* MTS_RegisterClient() {return new MTSClient;}
void MTS_DeregisterClient(MTSClient *c) {delete c;}
bool MTS_HasMaster(MTSClient *c) {return c ? c->hasMaster() : false;}
bool MTS_Client_ShouldUpdateLibrary(MTSClient *c) {return c ? c->shouldUpdateLibrary() : false;}
bool MTS_ShouldFilterNote(MTSClient *c, char midinote, char midichannel) {return c ? c->shouldFilterNote(midinote & 127, midichannel) : false;}
double MTS_NoteToFrequency(MTSClient *c, char midinote, char midichannel) {return c ? c->freq(midinote, midichannel) : (1.0 / global.iet[midinote & 127]);}
double MTS_RetuningAsRatio(MTSClient *c, char midinote, char midichannel) {return c ? c->ratio(midinote, midichannel) : 1.0;}
double MTS_RetuningInSemitones(MTSClient *c, char midinote, char midichannel) {return c ? c->semitones(midinote, midichannel) : 0.0;}
char MTS_FrequencyToNote(MTSClient *c, double freq, char midichannel) {return c ? c->freqToNote(freq, midichannel) : freqToNoteET(freq);}
char MTS_FrequencyToNoteAndChannel(MTSClient *c, double freq, char *midichannel) {if (c) return c->freqToNote(freq, midichannel); if (midichannel) *midichannel = 0; return freqToNoteET(freq);}
const char *MTS_GetScaleName(MTSClient *c) {return c ? c->getScaleName() : "";}
double MTS_GetPeriodRatio(MTSClient *c) {return c ? c->getPeriodRatio() : 2.0;}
double MTS_GetPeriodSemitones(MTSClient *c) {return c ? c->getPeriodSemitones() : 12.0;}
char MTS_GetMapSize(MTSClient *c) {return c ? c->getMapSize() : static_cast<char>(-1);}
char MTS_GetMapStartKey(MTSClient *c) {return c ? c->getMapStartKey() : static_cast<char>(-1);}
char MTS_GetRefKey(MTSClient *c) {return c ? c->getRefKey() : static_cast<char>(-1);}
void MTS_ParseMIDIDataU(MTSClient *c, const unsigned char *buffer, int len) {if (c) c->parseMIDIData(buffer, len);}
void MTS_ParseMIDIData(MTSClient *c, const char *buffer, int len) {if (c) c->parseMIDIData(reinterpret_cast<const unsigned char*>(buffer), len);}
bool MTS_HasReceivedMTSSysEx(MTSClient *c) {return c ? c->hasReceivedMTSSysEx() : false;}

189
External/MTS/libMTSClient.h vendored Normal file
View file

@ -0,0 +1,189 @@
/*
Copyright (C) 2021 by ODDSound Ltd. info@oddsound.com
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
*/
#ifndef libMTSClient_h
#define libMTSClient_h
#ifdef __cplusplus
extern "C" {
#endif
/*
Steps for using the MTS-ESP client API to add microtuning support to a plug-in.
Steps 1 and 2 are required, however it is recommended to include further steps when
integrating:
1. REQUIRED: Register and de-register a plug-in instance as a client with MTS-ESP.
On startup in the plug-in constructor call:
MTSClient *client = MTS_RegisterClient();
Store the returned MTSClient pointer to supply when calling other MTS-ESP client API
functions. On shutdown in the plug-in destructor call:
MTS_DeregisterClient(client);
2. REQUIRED: Query retuning when a note-on message is received and adjust tuning accordingly.
When given a note call:
double freq = MTS_NoteToFrequency(client, midinote, midichannel);
OR
double retune_semitones = MTS_RetuningInSemitones(client, midinote, midichannel);
OR
double retune_ratio = MTS_RetuningAsRatio(client, midinote, midichannel);
MIDI channel arguments should use the range [0,15] however if you dont know the MIDI
channel, use -1 (see step 6 for more on MIDI channels).
3. RECOMMENDED: Continuously query retuning whilst a note is held, allowing tuning to change
along the flight of a note. Do this if you can and as often as possible, ideally at the same
time as processing any other pitch modulation sources (envelopes, MIDI controllers, LFOs etc.).
4. RECOMMENDED: Provide an option to the user to select whether tuning is queried at note-on
only, as in step 2, or continuously, as in step 3. There are creative and practical
advantages to both, depending on the use case, and offering an option to the user will
provide the most useful MTS-ESP integration. If not offering such an option, continuous
retuning should be preferred over note-on only retuning.
5. RECOMMENDED: Query whether a note should be sounded when a note-on message is received.
The Scala .kbm keyboard mapping format allows for MIDI keys to be unmapped i.e. no frequency
is specified for them, and the MTS-ESP library supports this too. You can query whether a note
is unmapped and should be ignored with:
bool should_ignore_note = MTS_ShouldFilterNote(client, midinote, midichannel);
If this returns true, ignore the note-on and dont play anything. Calling this function is
recommended but optional and a valid value for frequency/retuning will be returned for an
unmapped note. MIDI channel arguments should use the range [0,15] however if you dont
know the MIDI channel, use -1.
6. RECOMMENDED: Always supply a MIDI channel when querying retuning or note filtering. Doing
so allows your plug-in to use multi-channel tuning tables, useful for microtonal MIDI controllers
with more than 128 keys or working with large scales. Even if multi-channel tables are not
in use, a master may still make use of channel-specific note filtering for functions such as
key switches to change tunings. If your plug-in supports MPE and has a switch for enabling MPE
support, it is recommended to NOT supply a MIDI channel if MPE is enabled.
7. RECOMMENDED: If you are adding MTS-ESP support to a plug-in that already has some kind
of microtuning support, e.g. loading .scl or .tun files, let the local tuning automatically
override MTS-ESP, or provide an option for MTS-ESP retuning to be explicitly disabled.
This affords a user the option to use a different tuning to the global MTS-ESP table
for a specific plug-in instance.
8. OPTIONAL: Add support for MIDI Tuning Standard (or MTS, from the MIDI specification) SysEx
messages to your plug-in. When not connected to an MTS-ESP master plug-in, these can be used
to retune it instead, providing microtuning support even when MTS-ESP is not in use.
When a SysEx message is received, call:
MTS_ParseMIDIData(client, buffer, len); // if buffer is signed char *
OR
MTS_ParseMIDIDataU(client, buffer, len); // if buffer is unsigned char *
These will update a local tuning table which is used when querying retuning as in steps 2
and 3. Check whether a valid MTS SysEx message has been received with:
bool MTS_SysEx_received = MTS_HasReceivedMTSSysEx(client);
9. OPTIONAL: If you want to display to the user whether the plug-in is "connected" to an
MTS-ESP master plug-in, call:
bool has_master = MTS_HasMaster(client);
10: OPTIONAL: It is possible to query the name of the current scale. This function is necessarily
supplied for the case where a client is sending MTS SysEx messages, however it can be used
to display the current scale name to the user on your UI too:
const char *name = MTS_GetScaleName(client);
11: OPTIONAL: After registering, let the user know if they have an older version of the libMTS dynamic library
installed which may not support some features in this version of the API:
bool should_update = MTS_Client_ShouldUpdateLibrary(client);
The latest version of libMTS will always be backward compatible with clients built with
an older version of the API. Users can update libMTS using the installers at
https://github.com/ODDSound/MTS-ESP/tree/main/libMTS.
12: EXTRAS: Helper functions are available which return the MIDI note whose pitch is nearest
a given frequency. The MIDI note returned is guaranteed to be mapped. If you intend to
generate a note-on message using the returned note number, you may already know which MIDI
channel it will be sent on, in which case you must specify this in the call, else the client
library can prescribe a channel for you. This is done so that multi-channel mapping
and note filtering can be respected. See below for further details.
*/
// Opaque datatype for MTSClient.
typedef struct MTSClient MTSClient;
// Register/deregister as a client. Call from the plug-in constructor and destructor.
extern MTSClient *MTS_RegisterClient();
extern void MTS_DeregisterClient(MTSClient *client);
// Check if the client is currently connected to a master plug-in.
extern bool MTS_HasMaster(MTSClient *client);
// Check if the MTS-ESP dynamic library needs to be updated to use all features in this version of the API.
extern bool MTS_Client_ShouldUpdateLibrary(MTSClient *client);
// Returns true if note should not be played. MIDI channel argument should be included if possible (0-15), else set to -1.
extern bool MTS_ShouldFilterNote(MTSClient *client, char midinote, char midichannel);
// Retuning a midi note. Pick the version that makes your life easiest! MIDI channel argument should be included if possible (0-15), else set to -1.
extern double MTS_NoteToFrequency(MTSClient *client, char midinote, char midichannel);
extern double MTS_RetuningInSemitones(MTSClient *client, char midinote, char midichannel);
extern double MTS_RetuningAsRatio(MTSClient *client, char midinote, char midichannel);
// MTS_FrequencyToNote() is a helper function returning the note number whose pitch is closest to the supplied frequency. Two versions are provided:
// The first is for the simplest case: supply a frequency and get a note number back.
// If you intend to use the returned note number to generate a note-on message on a specific, pre-determined MIDI channel, set the midichannel argument to the destination channel (0-15), else set to -1.
// If a MIDI channel is supplied, the corresponding multi-channel tuning table will be queried if in use, else multi-channel tables are ignored.
extern char MTS_FrequencyToNote(MTSClient *client, double freq, char midichannel);
// Use the second version if you intend to use the returned note number to generate a note-on message and where you have the possibility to send it on any MIDI channel.
// The midichannel argument is a pointer to a char which will receive the MIDI channel on which the note message should be sent (0-15).
// Multi-channel tuning tables are queried if in use.
extern char MTS_FrequencyToNoteAndChannel(MTSClient *client, double freq, char *midichannel);
// Returns the name of the current scale.
extern const char *MTS_GetScaleName(MTSClient *client);
// Returns the period of the current scale, or 2.0 (12 semitones) if not supplied by a master.
extern double MTS_GetPeriodRatio(MTSClient *client);
extern double MTS_GetPeriodSemitones(MTSClient *client);
// Query information about keyboard mapping.
// NOTE: negative values are invalid and these functions will return -1 if the information has not been supplied by a master.
// The return value must therefore be checked it is valid before being used.
extern char MTS_GetMapSize(MTSClient *client);
extern char MTS_GetMapStartKey(MTSClient *client);
extern char MTS_GetRefKey(MTSClient *client);
// Parse incoming MIDI data to update local tuning. All formats of MTS SysEx message accepted.
extern void MTS_ParseMIDIDataU(MTSClient *client, const unsigned char *buffer, int len);
extern void MTS_ParseMIDIData(MTSClient *client, const char *buffer, int len);
// Check if the client has received any valid MTS SysEx messages and will use local tuning if not connected to a master plug-in.
extern bool MTS_HasReceivedMTSSysEx(MTSClient *client);
#ifdef __cplusplus
}
#endif
#endif

26
External/readerwriterqueue/.gitignore vendored Normal file
View file

@ -0,0 +1,26 @@
*.ipch
*.suo
*.user
*.sdf
*.opensdf
*.exe
*.VC.db
.vs/
tests/stabtest/msvc*/Debug/
tests/stabtest/msvc*/Release/
tests/stabtest/msvc*/obj/
tests/stabtest/msvc*/log.txt
tests/stabtest/log.txt
tests/unittests/msvc*/Debug/
tests/unittests/msvc*/Release/
tests/unittests/msvc*/obj/
tests/CDSChecker/model-checker/
benchmarks/msvc*/Debug/
benchmarks/msvc*/Release/
benchmarks/msvc*/obj/
test/
# Linux binaries
benchmarks/benchmarks
tests/stabtest/stabtest
tests/unittests/unittests

View file

@ -0,0 +1,70 @@
# See https://discourse.cmake.org/t/how-to-fix-cmake-minimum-required-deprecation-warning/2487/2
# for more on setting the minimum required version.
cmake_minimum_required(VERSION 3.9...3.31.7)
project(readerwriterqueue VERSION 1.0.7)
include(GNUInstallDirs)
include(CMakePackageConfigHelpers)
add_library(${PROJECT_NAME} INTERFACE)
target_include_directories(readerwriterqueue INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}/>
)
install(FILES atomicops.h readerwriterqueue.h readerwritercircularbuffer.h LICENSE.md
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME})
install(TARGETS ${PROJECT_NAME}
EXPORT ${PROJECT_NAME}Targets
)
write_basic_package_version_file(
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake
VERSION
${PROJECT_VERSION}
COMPATIBILITY AnyNewerVersion
ARCH_INDEPENDENT
)
configure_package_config_file(${PROJECT_NAME}Config.cmake.in
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake
INSTALL_DESTINATION
${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}/
)
install(EXPORT
${PROJECT_NAME}Targets
FILE
${PROJECT_NAME}Targets.cmake
NAMESPACE
"${PROJECT_NAME}::"
DESTINATION
${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}
COMPONENT
Devel
)
install(
FILES
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake
DESTINATION
${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}
COMPONENT
Devel
)
set(CPACK_PACKAGE_NAME ${PROJECT_NAME})
set(CPACK_PACKAGE_VENDOR "Cameron Desrochers <cameron@moodycamel.com>")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "A single-producer, single-consumer lock-free queue for C++.")
set(CPACK_PACKAGE_VERSION "${PROJECT_VERSION}")
set(CPACK_PACKAGE_VERSION_MAJOR "${PROJECT_VERSION_MAJOR}")
set(CPACK_PACKAGE_VERSION_MINOR "${PROJECT_VERSION_MINOR}")
set(CPACK_PACKAGE_VERSION_PATCH "${PROJECT_VERSION_PATCH}")
set(CPACK_DEBIAN_PACKAGE_MAINTAINER ${CPACK_PACKAGE_VENDOR})
set(CPACK_GENERATOR "RPM;DEB")
include(CPack)

28
External/readerwriterqueue/LICENSE.md vendored Normal file
View file

@ -0,0 +1,28 @@
This license applies to all the code in this repository except that written by third
parties, namely the files in benchmarks/ext, which have their own licenses, and Jeff
Preshing's semaphore implementation (used in the blocking queues) which has a zlib
license (embedded in atomicops.h).
Simplified BSD License:
Copyright (c) 2013-2021, Cameron Desrochers
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
- Redistributions of source code must retain the above copyright notice, this list of
conditions and the following disclaimer.
- Redistributions in binary form must reproduce the above copyright notice, this list of
conditions and the following disclaimer in the documentation and/or other materials
provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

186
External/readerwriterqueue/README.md vendored Normal file
View file

@ -0,0 +1,186 @@
# A single-producer, single-consumer lock-free queue for C++
This mini-repository has my very own implementation of a lock-free queue (that I designed from scratch) for C++.
It only supports a two-thread use case (one consuming, and one producing). The threads can't switch roles, though
you could use this queue completely from a single thread if you wish (but that would sort of defeat the purpose!).
Note: If you need a general-purpose multi-producer, multi-consumer lock free queue, I have [one of those too][mpmc].
This repository also includes a [circular-buffer SPSC queue][circular] which supports blocking on enqueue as well as dequeue.
## Features
- [Blazing fast][benchmarks]
- Compatible with C++11 (supports moving objects instead of making copies)
- Fully generic (templated container of any type) -- just like `std::queue`, you never need to allocate memory for elements yourself
(which saves you the hassle of writing a lock-free memory manager to hold the elements you're queueing)
- Allocates memory up front, in contiguous blocks
- Provides a `try_enqueue` method which is guaranteed never to allocate memory (the queue starts with an initial capacity)
- Also provides an `enqueue` method which can dynamically grow the size of the queue as needed
- Also provides `try_emplace`/`emplace` convenience methods
- Has a blocking version with `wait_dequeue`
- Completely "wait-free" (no compare-and-swap loop). Enqueue and dequeue are always O(1) (not counting memory allocation)
- On x86, the memory barriers compile down to no-ops, meaning enqueue and dequeue are just a simple series of loads and stores (and branches)
## Use
Simply drop the readerwriterqueue.h (or readerwritercircularbuffer.h) and atomicops.h files into your source code and include them :-)
A modern compiler is required (MSVC2010+, GCC 4.7+, ICC 13+, or any C++11 compliant compiler should work).
Note: If you're using GCC, you really do need GCC 4.7 or above -- [4.6 has a bug][gcc46bug] that prevents the atomic fence primitives
from working correctly.
Example:
```cpp
using namespace moodycamel;
ReaderWriterQueue<int> q(100); // Reserve space for at least 100 elements up front
q.enqueue(17); // Will allocate memory if the queue is full
bool succeeded = q.try_enqueue(18); // Will only succeed if the queue has an empty slot (never allocates)
assert(succeeded);
int number;
succeeded = q.try_dequeue(number); // Returns false if the queue was empty
assert(succeeded && number == 17);
// You can also peek at the front item of the queue (consumer only)
int* front = q.peek();
assert(*front == 18);
succeeded = q.try_dequeue(number);
assert(succeeded && number == 18);
front = q.peek();
assert(front == nullptr); // Returns nullptr if the queue was empty
```
The blocking version has the exact same API, with the addition of `wait_dequeue` and
`wait_dequeue_timed` methods:
```cpp
BlockingReaderWriterQueue<int> q;
std::thread reader([&]() {
int item;
#if 1
for (int i = 0; i != 100; ++i) {
// Fully-blocking:
q.wait_dequeue(item);
}
#else
for (int i = 0; i != 100; ) {
// Blocking with timeout
if (q.wait_dequeue_timed(item, std::chrono::milliseconds(5)))
++i;
}
#endif
});
std::thread writer([&]() {
for (int i = 0; i != 100; ++i) {
q.enqueue(i);
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
});
writer.join();
reader.join();
assert(q.size_approx() == 0);
```
Note that `wait_dequeue` will block indefinitely while the queue is empty; this
means care must be taken to only call `wait_dequeue` if you're sure another element
will come along eventually, or if the queue has a static lifetime. This is because
destroying the queue while a thread is waiting on it will invoke undefined behaviour.
The blocking circular buffer has a fixed number of slots, but is otherwise quite similar to
use:
```cpp
BlockingReaderWriterCircularBuffer<int> q(1024); // pass initial capacity
q.try_enqueue(1);
int number;
q.try_dequeue(number);
assert(number == 1);
q.wait_enqueue(123);
q.wait_dequeue(number);
assert(number == 123);
q.wait_dequeue_timed(number, std::chrono::milliseconds(10));
```
## CMake
### Using targets in your project
Using this project as a part of an existing CMake project is easy.
In your CMakeLists.txt:
```
include(FetchContent)
FetchContent_Declare(
readerwriterqueue
GIT_REPOSITORY https://github.com/cameron314/readerwriterqueue
GIT_TAG master
)
FetchContent_MakeAvailable(readerwriterqueue)
add_library(my_target main.cpp)
target_link_libraries(my_target PUBLIC readerwriterqueue)
```
In main.cpp:
```cpp
#include <readerwriterqueue.h>
int main()
{
moodycamel::ReaderWriterQueue<int> q(100);
}
```
### Installing into system directories
As an alternative to including the source files in your project directly,
you can use CMake to install the library in your system's include directory:
```
mkdir build
cd build
cmake ..
make install
```
Then, you can include it from your source code:
```
#include <readerwriterqueue/readerwriterqueue.h>
```
## Disclaimers
The queue should only be used on platforms where aligned integer and pointer access is atomic; fortunately, that
includes all modern processors (e.g. x86/x86-64, ARM, and PowerPC). *Not* for use with a DEC Alpha processor (which has very weak memory ordering) :-)
Note that it's only been tested on x86(-64); if someone has access to other processors I'd love to run some tests on
anything that's not x86-based.
## More info
See the [LICENSE.md][license] file for the license (simplified BSD).
My [blog post][blog] introduces the context that led to this code, and may be of interest if you're curious
about lock-free programming.
[blog]: http://moodycamel.com/blog/2013/a-fast-lock-free-queue-for-c++
[license]: LICENSE.md
[benchmarks]: http://moodycamel.com/blog/2013/a-fast-lock-free-queue-for-c++#benchmarks
[gcc46bug]: http://stackoverflow.com/questions/16429669/stdatomic-thread-fence-has-undefined-reference
[mpmc]: https://github.com/cameron314/concurrentqueue
[circular]: readerwritercircularbuffer.h

772
External/readerwriterqueue/atomicops.h vendored Normal file
View file

@ -0,0 +1,772 @@
// ©2013-2016 Cameron Desrochers.
// Distributed under the simplified BSD license (see the license file that
// should have come with this header).
// Uses Jeff Preshing's semaphore implementation (under the terms of its
// separate zlib license, embedded below).
#pragma once
// Provides portable (VC++2010+, Intel ICC 13, GCC 4.7+, and anything C++11 compliant) implementation
// of low-level memory barriers, plus a few semi-portable utility macros (for inlining and alignment).
// Also has a basic atomic type (limited to hardware-supported atomics with no memory ordering guarantees).
// Uses the AE_* prefix for macros (historical reasons), and the "moodycamel" namespace for symbols.
#include <cerrno>
#include <cassert>
#include <type_traits>
#include <cerrno>
#include <cstdint>
#include <ctime>
// Platform detection
#if defined(__INTEL_COMPILER)
#define AE_ICC
#elif defined(_MSC_VER)
#define AE_VCPP
#elif defined(__GNUC__)
#define AE_GCC
#endif
#if defined(_M_IA64) || defined(__ia64__)
#define AE_ARCH_IA64
#elif defined(_WIN64) || defined(__amd64__) || defined(_M_X64) || defined(__x86_64__)
#define AE_ARCH_X64
#elif defined(_M_IX86) || defined(__i386__)
#define AE_ARCH_X86
#elif defined(_M_PPC) || defined(__powerpc__)
#define AE_ARCH_PPC
#else
#define AE_ARCH_UNKNOWN
#endif
// AE_UNUSED
#define AE_UNUSED(x) ((void)x)
// AE_NO_TSAN/AE_TSAN_ANNOTATE_*
// For GCC
#if defined(__SANITIZE_THREAD__)
#define AE_TSAN_IS_ENABLED
#endif
// For clang
#if defined(__has_feature)
#if __has_feature(thread_sanitizer) && !defined(AE_TSAN_IS_ENABLED)
#define AE_TSAN_IS_ENABLED
#endif
#endif
#ifdef AE_TSAN_IS_ENABLED
#if __cplusplus >= 201703L // inline variables require C++17
namespace moodycamel { inline int ae_tsan_global; }
#define AE_TSAN_ANNOTATE_RELEASE() AnnotateHappensBefore(__FILE__, __LINE__, (void *)(&::moodycamel::ae_tsan_global))
#define AE_TSAN_ANNOTATE_ACQUIRE() AnnotateHappensAfter(__FILE__, __LINE__, (void *)(&::moodycamel::ae_tsan_global))
extern "C" void AnnotateHappensBefore(const char*, int, void*);
extern "C" void AnnotateHappensAfter(const char*, int, void*);
#else // when we can't work with tsan, attempt to disable its warnings
#define AE_NO_TSAN __attribute__((no_sanitize("thread")))
#endif
#endif
#ifndef AE_NO_TSAN
#define AE_NO_TSAN
#endif
#ifndef AE_TSAN_ANNOTATE_RELEASE
#define AE_TSAN_ANNOTATE_RELEASE()
#define AE_TSAN_ANNOTATE_ACQUIRE()
#endif
// AE_FORCEINLINE
#if defined(AE_VCPP) || defined(AE_ICC)
#define AE_FORCEINLINE __forceinline
#elif defined(AE_GCC)
//#define AE_FORCEINLINE __attribute__((always_inline))
#define AE_FORCEINLINE inline
#else
#define AE_FORCEINLINE inline
#endif
// AE_ALIGN
#if defined(AE_VCPP) || defined(AE_ICC)
#define AE_ALIGN(x) __declspec(align(x))
#elif defined(AE_GCC)
#define AE_ALIGN(x) __attribute__((aligned(x)))
#else
// Assume GCC compliant syntax...
#define AE_ALIGN(x) __attribute__((aligned(x)))
#endif
// Portable atomic fences implemented below:
namespace moodycamel {
enum memory_order {
memory_order_relaxed,
memory_order_acquire,
memory_order_release,
memory_order_acq_rel,
memory_order_seq_cst,
// memory_order_sync: Forces a full sync:
// #LoadLoad, #LoadStore, #StoreStore, and most significantly, #StoreLoad
memory_order_sync = memory_order_seq_cst
};
} // end namespace moodycamel
#if (defined(AE_VCPP) && (_MSC_VER < 1700 || defined(__cplusplus_cli))) || (defined(AE_ICC) && __INTEL_COMPILER < 1600)
// VS2010 and ICC13 don't support std::atomic_*_fence, implement our own fences
#include <intrin.h>
#if defined(AE_ARCH_X64) || defined(AE_ARCH_X86)
#define AeFullSync _mm_mfence
#define AeLiteSync _mm_mfence
#elif defined(AE_ARCH_IA64)
#define AeFullSync __mf
#define AeLiteSync __mf
#elif defined(AE_ARCH_PPC)
#include <ppcintrinsics.h>
#define AeFullSync __sync
#define AeLiteSync __lwsync
#endif
#ifdef AE_VCPP
#pragma warning(push)
#pragma warning(disable: 4365) // Disable erroneous 'conversion from long to unsigned int, signed/unsigned mismatch' error when using `assert`
#ifdef __cplusplus_cli
#pragma managed(push, off)
#endif
#endif
namespace moodycamel {
AE_FORCEINLINE void compiler_fence(memory_order order) AE_NO_TSAN
{
switch (order) {
case memory_order_relaxed: break;
case memory_order_acquire: _ReadBarrier(); break;
case memory_order_release: _WriteBarrier(); break;
case memory_order_acq_rel: _ReadWriteBarrier(); break;
case memory_order_seq_cst: _ReadWriteBarrier(); break;
default: assert(false);
}
}
// x86/x64 have a strong memory model -- all loads and stores have
// acquire and release semantics automatically (so only need compiler
// barriers for those).
#if defined(AE_ARCH_X86) || defined(AE_ARCH_X64)
AE_FORCEINLINE void fence(memory_order order) AE_NO_TSAN
{
switch (order) {
case memory_order_relaxed: break;
case memory_order_acquire: _ReadBarrier(); break;
case memory_order_release: _WriteBarrier(); break;
case memory_order_acq_rel: _ReadWriteBarrier(); break;
case memory_order_seq_cst:
_ReadWriteBarrier();
AeFullSync();
_ReadWriteBarrier();
break;
default: assert(false);
}
}
#else
AE_FORCEINLINE void fence(memory_order order) AE_NO_TSAN
{
// Non-specialized arch, use heavier memory barriers everywhere just in case :-(
switch (order) {
case memory_order_relaxed:
break;
case memory_order_acquire:
_ReadBarrier();
AeLiteSync();
_ReadBarrier();
break;
case memory_order_release:
_WriteBarrier();
AeLiteSync();
_WriteBarrier();
break;
case memory_order_acq_rel:
_ReadWriteBarrier();
AeLiteSync();
_ReadWriteBarrier();
break;
case memory_order_seq_cst:
_ReadWriteBarrier();
AeFullSync();
_ReadWriteBarrier();
break;
default: assert(false);
}
}
#endif
} // end namespace moodycamel
#else
// Use standard library of atomics
#include <atomic>
namespace moodycamel {
AE_FORCEINLINE void compiler_fence(memory_order order) AE_NO_TSAN
{
switch (order) {
case memory_order_relaxed: break;
case memory_order_acquire: std::atomic_signal_fence(std::memory_order_acquire); break;
case memory_order_release: std::atomic_signal_fence(std::memory_order_release); break;
case memory_order_acq_rel: std::atomic_signal_fence(std::memory_order_acq_rel); break;
case memory_order_seq_cst: std::atomic_signal_fence(std::memory_order_seq_cst); break;
default: assert(false);
}
}
AE_FORCEINLINE void fence(memory_order order) AE_NO_TSAN
{
switch (order) {
case memory_order_relaxed: break;
case memory_order_acquire: AE_TSAN_ANNOTATE_ACQUIRE(); std::atomic_thread_fence(std::memory_order_acquire); break;
case memory_order_release: AE_TSAN_ANNOTATE_RELEASE(); std::atomic_thread_fence(std::memory_order_release); break;
case memory_order_acq_rel: AE_TSAN_ANNOTATE_ACQUIRE(); AE_TSAN_ANNOTATE_RELEASE(); std::atomic_thread_fence(std::memory_order_acq_rel); break;
case memory_order_seq_cst: AE_TSAN_ANNOTATE_ACQUIRE(); AE_TSAN_ANNOTATE_RELEASE(); std::atomic_thread_fence(std::memory_order_seq_cst); break;
default: assert(false);
}
}
} // end namespace moodycamel
#endif
#if !defined(AE_VCPP) || (_MSC_VER >= 1700 && !defined(__cplusplus_cli))
#define AE_USE_STD_ATOMIC_FOR_WEAK_ATOMIC
#endif
#ifdef AE_USE_STD_ATOMIC_FOR_WEAK_ATOMIC
#include <atomic>
#endif
#include <utility>
// WARNING: *NOT* A REPLACEMENT FOR std::atomic. READ CAREFULLY:
// Provides basic support for atomic variables -- no memory ordering guarantees are provided.
// The guarantee of atomicity is only made for types that already have atomic load and store guarantees
// at the hardware level -- on most platforms this generally means aligned pointers and integers (only).
namespace moodycamel {
template<typename T>
class weak_atomic
{
public:
AE_NO_TSAN weak_atomic() : value() { }
#ifdef AE_VCPP
#pragma warning(push)
#pragma warning(disable: 4100) // Get rid of (erroneous) 'unreferenced formal parameter' warning
#endif
template<typename U> AE_NO_TSAN weak_atomic(U&& x) : value(std::forward<U>(x)) { }
#ifdef __cplusplus_cli
// Work around bug with universal reference/nullptr combination that only appears when /clr is on
AE_NO_TSAN weak_atomic(nullptr_t) : value(nullptr) { }
#endif
AE_NO_TSAN weak_atomic(weak_atomic const& other) : value(other.load()) { }
AE_NO_TSAN weak_atomic(weak_atomic&& other) : value(std::move(other.load())) { }
#ifdef AE_VCPP
#pragma warning(pop)
#endif
AE_FORCEINLINE operator T() const AE_NO_TSAN { return load(); }
#ifndef AE_USE_STD_ATOMIC_FOR_WEAK_ATOMIC
template<typename U> AE_FORCEINLINE weak_atomic const& operator=(U&& x) AE_NO_TSAN { value = std::forward<U>(x); return *this; }
AE_FORCEINLINE weak_atomic const& operator=(weak_atomic const& other) AE_NO_TSAN { value = other.value; return *this; }
AE_FORCEINLINE T load() const AE_NO_TSAN { return value; }
AE_FORCEINLINE T fetch_add_acquire(T increment) AE_NO_TSAN
{
#if defined(AE_ARCH_X64) || defined(AE_ARCH_X86)
if (sizeof(T) == 4) return _InterlockedExchangeAdd((long volatile*)&value, (long)increment);
#if defined(_M_AMD64)
else if (sizeof(T) == 8) return _InterlockedExchangeAdd64((long long volatile*)&value, (long long)increment);
#endif
#else
#error Unsupported platform
#endif
assert(false && "T must be either a 32 or 64 bit type");
return value;
}
AE_FORCEINLINE T fetch_add_release(T increment) AE_NO_TSAN
{
#if defined(AE_ARCH_X64) || defined(AE_ARCH_X86)
if (sizeof(T) == 4) return _InterlockedExchangeAdd((long volatile*)&value, (long)increment);
#if defined(_M_AMD64)
else if (sizeof(T) == 8) return _InterlockedExchangeAdd64((long long volatile*)&value, (long long)increment);
#endif
#else
#error Unsupported platform
#endif
assert(false && "T must be either a 32 or 64 bit type");
return value;
}
#else
template<typename U>
AE_FORCEINLINE weak_atomic const& operator=(U&& x) AE_NO_TSAN
{
value.store(std::forward<U>(x), std::memory_order_relaxed);
return *this;
}
AE_FORCEINLINE weak_atomic const& operator=(weak_atomic const& other) AE_NO_TSAN
{
value.store(other.value.load(std::memory_order_relaxed), std::memory_order_relaxed);
return *this;
}
AE_FORCEINLINE T load() const AE_NO_TSAN { return value.load(std::memory_order_relaxed); }
AE_FORCEINLINE T fetch_add_acquire(T increment) AE_NO_TSAN
{
return value.fetch_add(increment, std::memory_order_acquire);
}
AE_FORCEINLINE T fetch_add_release(T increment) AE_NO_TSAN
{
return value.fetch_add(increment, std::memory_order_release);
}
#endif
private:
#ifndef AE_USE_STD_ATOMIC_FOR_WEAK_ATOMIC
// No std::atomic support, but still need to circumvent compiler optimizations.
// `volatile` will make memory access slow, but is guaranteed to be reliable.
volatile T value;
#else
std::atomic<T> value;
#endif
};
} // end namespace moodycamel
// Portable single-producer, single-consumer semaphore below:
#if defined(_WIN32)
// Avoid including windows.h in a header; we only need a handful of
// items, so we'll redeclare them here (this is relatively safe since
// the API generally has to remain stable between Windows versions).
// I know this is an ugly hack but it still beats polluting the global
// namespace with thousands of generic names or adding a .cpp for nothing.
extern "C" {
struct _SECURITY_ATTRIBUTES;
__declspec(dllimport) void* __stdcall CreateSemaphoreW(_SECURITY_ATTRIBUTES* lpSemaphoreAttributes, long lInitialCount, long lMaximumCount, const wchar_t* lpName);
__declspec(dllimport) int __stdcall CloseHandle(void* hObject);
__declspec(dllimport) unsigned long __stdcall WaitForSingleObject(void* hHandle, unsigned long dwMilliseconds);
__declspec(dllimport) int __stdcall ReleaseSemaphore(void* hSemaphore, long lReleaseCount, long* lpPreviousCount);
}
#elif defined(__MACH__)
#include <mach/mach.h>
#elif defined(__unix__)
#include <semaphore.h>
#elif defined(FREERTOS)
#include <FreeRTOS.h>
#include <semphr.h>
#include <task.h>
#endif
namespace moodycamel
{
// Code in the spsc_sema namespace below is an adaptation of Jeff Preshing's
// portable + lightweight semaphore implementations, originally from
// https://github.com/preshing/cpp11-on-multicore/blob/master/common/sema.h
// LICENSE:
// Copyright (c) 2015 Jeff Preshing
//
// This software is provided 'as-is', without any express or implied
// warranty. In no event will the authors be held liable for any damages
// arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not
// claim that you wrote the original software. If you use this software
// in a product, an acknowledgement in the product documentation would be
// appreciated but is not required.
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
// 3. This notice may not be removed or altered from any source distribution.
namespace spsc_sema
{
#if defined(_WIN32)
class Semaphore
{
private:
void* m_hSema;
Semaphore(const Semaphore& other);
Semaphore& operator=(const Semaphore& other);
public:
AE_NO_TSAN Semaphore(int initialCount = 0) : m_hSema()
{
assert(initialCount >= 0);
const long maxLong = 0x7fffffff;
m_hSema = CreateSemaphoreW(nullptr, initialCount, maxLong, nullptr);
assert(m_hSema);
}
AE_NO_TSAN ~Semaphore()
{
CloseHandle(m_hSema);
}
bool wait() AE_NO_TSAN
{
const unsigned long infinite = 0xffffffff;
return WaitForSingleObject(m_hSema, infinite) == 0;
}
bool try_wait() AE_NO_TSAN
{
return WaitForSingleObject(m_hSema, 0) == 0;
}
bool timed_wait(std::uint64_t usecs) AE_NO_TSAN
{
return WaitForSingleObject(m_hSema, (unsigned long)(usecs / 1000)) == 0;
}
void signal(int count = 1) AE_NO_TSAN
{
while (!ReleaseSemaphore(m_hSema, count, nullptr));
}
};
#elif defined(__MACH__)
//---------------------------------------------------------
// Semaphore (Apple iOS and OSX)
// Can't use POSIX semaphores due to http://lists.apple.com/archives/darwin-kernel/2009/Apr/msg00010.html
//---------------------------------------------------------
class Semaphore
{
private:
semaphore_t m_sema;
Semaphore(const Semaphore& other);
Semaphore& operator=(const Semaphore& other);
public:
AE_NO_TSAN Semaphore(int initialCount = 0) : m_sema()
{
assert(initialCount >= 0);
kern_return_t rc = semaphore_create(mach_task_self(), &m_sema, SYNC_POLICY_FIFO, initialCount);
assert(rc == KERN_SUCCESS);
AE_UNUSED(rc);
}
AE_NO_TSAN ~Semaphore()
{
semaphore_destroy(mach_task_self(), m_sema);
}
bool wait() AE_NO_TSAN
{
return semaphore_wait(m_sema) == KERN_SUCCESS;
}
bool try_wait() AE_NO_TSAN
{
return timed_wait(0);
}
bool timed_wait(std::uint64_t timeout_usecs) AE_NO_TSAN
{
mach_timespec_t ts;
ts.tv_sec = static_cast<unsigned int>(timeout_usecs / 1000000);
ts.tv_nsec = static_cast<int>((timeout_usecs % 1000000) * 1000);
// added in OSX 10.10: https://developer.apple.com/library/prerelease/mac/documentation/General/Reference/APIDiffsMacOSX10_10SeedDiff/modules/Darwin.html
kern_return_t rc = semaphore_timedwait(m_sema, ts);
return rc == KERN_SUCCESS;
}
void signal() AE_NO_TSAN
{
while (semaphore_signal(m_sema) != KERN_SUCCESS);
}
void signal(int count) AE_NO_TSAN
{
while (count-- > 0)
{
while (semaphore_signal(m_sema) != KERN_SUCCESS);
}
}
};
#elif defined(__unix__)
//---------------------------------------------------------
// Semaphore (POSIX, Linux)
//---------------------------------------------------------
class Semaphore
{
private:
sem_t m_sema;
Semaphore(const Semaphore& other);
Semaphore& operator=(const Semaphore& other);
public:
AE_NO_TSAN Semaphore(int initialCount = 0) : m_sema()
{
assert(initialCount >= 0);
int rc = sem_init(&m_sema, 0, static_cast<unsigned int>(initialCount));
assert(rc == 0);
AE_UNUSED(rc);
}
AE_NO_TSAN ~Semaphore()
{
sem_destroy(&m_sema);
}
bool wait() AE_NO_TSAN
{
// http://stackoverflow.com/questions/2013181/gdb-causes-sem-wait-to-fail-with-eintr-error
int rc;
do
{
rc = sem_wait(&m_sema);
}
while (rc == -1 && errno == EINTR);
return rc == 0;
}
bool try_wait() AE_NO_TSAN
{
int rc;
do {
rc = sem_trywait(&m_sema);
} while (rc == -1 && errno == EINTR);
return rc == 0;
}
bool timed_wait(std::uint64_t usecs) AE_NO_TSAN
{
struct timespec ts;
const int usecs_in_1_sec = 1000000;
const int nsecs_in_1_sec = 1000000000;
clock_gettime(CLOCK_REALTIME, &ts);
ts.tv_sec += static_cast<time_t>(usecs / usecs_in_1_sec);
ts.tv_nsec += static_cast<long>(usecs % usecs_in_1_sec) * 1000;
// sem_timedwait bombs if you have more than 1e9 in tv_nsec
// so we have to clean things up before passing it in
if (ts.tv_nsec >= nsecs_in_1_sec) {
ts.tv_nsec -= nsecs_in_1_sec;
++ts.tv_sec;
}
int rc;
do {
rc = sem_timedwait(&m_sema, &ts);
} while (rc == -1 && errno == EINTR);
return rc == 0;
}
void signal() AE_NO_TSAN
{
while (sem_post(&m_sema) == -1);
}
void signal(int count) AE_NO_TSAN
{
while (count-- > 0)
{
while (sem_post(&m_sema) == -1);
}
}
};
#elif defined(FREERTOS)
//---------------------------------------------------------
// Semaphore (FreeRTOS)
//---------------------------------------------------------
class Semaphore
{
private:
SemaphoreHandle_t m_sema;
Semaphore(const Semaphore& other);
Semaphore& operator=(const Semaphore& other);
public:
AE_NO_TSAN Semaphore(int initialCount = 0) : m_sema()
{
assert(initialCount >= 0);
m_sema = xSemaphoreCreateCounting(static_cast<UBaseType_t>(~0ull), static_cast<UBaseType_t>(initialCount));
assert(m_sema);
}
AE_NO_TSAN ~Semaphore()
{
vSemaphoreDelete(m_sema);
}
bool wait() AE_NO_TSAN
{
return xSemaphoreTake(m_sema, portMAX_DELAY) == pdTRUE;
}
bool try_wait() AE_NO_TSAN
{
// Note: In an ISR context, if this causes a task to unblock,
// the caller won't know about it
if (xPortIsInsideInterrupt())
return xSemaphoreTakeFromISR(m_sema, NULL) == pdTRUE;
return xSemaphoreTake(m_sema, 0) == pdTRUE;
}
bool timed_wait(std::uint64_t usecs) AE_NO_TSAN
{
std::uint64_t msecs = usecs / 1000;
TickType_t ticks = static_cast<TickType_t>(msecs / portTICK_PERIOD_MS);
if (ticks == 0)
return try_wait();
return xSemaphoreTake(m_sema, ticks) == pdTRUE;
}
void signal() AE_NO_TSAN
{
// Note: In an ISR context, if this causes a task to unblock,
// the caller won't know about it
BaseType_t rc;
if (xPortIsInsideInterrupt())
rc = xSemaphoreGiveFromISR(m_sema, NULL);
else
rc = xSemaphoreGive(m_sema);
assert(rc == pdTRUE);
AE_UNUSED(rc);
}
void signal(int count) AE_NO_TSAN
{
while (count-- > 0)
signal();
}
};
#else
#error Unsupported platform! (No semaphore wrapper available)
#endif
//---------------------------------------------------------
// LightweightSemaphore
//---------------------------------------------------------
class LightweightSemaphore
{
public:
typedef std::make_signed<std::size_t>::type ssize_t;
private:
weak_atomic<ssize_t> m_count;
Semaphore m_sema;
bool waitWithPartialSpinning(std::int64_t timeout_usecs = -1) AE_NO_TSAN
{
ssize_t oldCount;
// Is there a better way to set the initial spin count?
// If we lower it to 1000, testBenaphore becomes 15x slower on my Core i7-5930K Windows PC,
// as threads start hitting the kernel semaphore.
int spin = 1024;
while (--spin >= 0)
{
if (m_count.load() > 0)
{
m_count.fetch_add_acquire(-1);
return true;
}
compiler_fence(memory_order_acquire); // Prevent the compiler from collapsing the loop.
}
oldCount = m_count.fetch_add_acquire(-1);
if (oldCount > 0)
return true;
if (timeout_usecs < 0)
{
if (m_sema.wait())
return true;
}
if (timeout_usecs > 0 && m_sema.timed_wait(static_cast<uint64_t>(timeout_usecs)))
return true;
// At this point, we've timed out waiting for the semaphore, but the
// count is still decremented indicating we may still be waiting on
// it. So we have to re-adjust the count, but only if the semaphore
// wasn't signaled enough times for us too since then. If it was, we
// need to release the semaphore too.
while (true)
{
oldCount = m_count.fetch_add_release(1);
if (oldCount < 0)
return false; // successfully restored things to the way they were
// Oh, the producer thread just signaled the semaphore after all. Try again:
oldCount = m_count.fetch_add_acquire(-1);
if (oldCount > 0 && m_sema.try_wait())
return true;
}
}
public:
AE_NO_TSAN LightweightSemaphore(ssize_t initialCount = 0) : m_count(initialCount), m_sema()
{
assert(initialCount >= 0);
}
bool tryWait() AE_NO_TSAN
{
if (m_count.load() > 0)
{
m_count.fetch_add_acquire(-1);
return true;
}
return false;
}
bool wait() AE_NO_TSAN
{
return tryWait() || waitWithPartialSpinning();
}
bool wait(std::int64_t timeout_usecs) AE_NO_TSAN
{
return tryWait() || waitWithPartialSpinning(timeout_usecs);
}
void signal(ssize_t count = 1) AE_NO_TSAN
{
assert(count >= 0);
ssize_t oldCount = m_count.fetch_add_release(count);
assert(oldCount >= -1);
if (oldCount < 0)
{
m_sema.signal(1);
}
}
std::size_t availableApprox() const AE_NO_TSAN
{
ssize_t count = m_count.load();
return count > 0 ? static_cast<std::size_t>(count) : 0;
}
};
} // end namespace spsc_sema
} // end namespace moodycamel
#if defined(AE_VCPP) && (_MSC_VER < 1700 || defined(__cplusplus_cli))
#pragma warning(pop)
#ifdef __cplusplus_cli
#pragma managed(pop)
#endif
#endif

View file

@ -0,0 +1,472 @@
// ©2013-2015 Cameron Desrochers.
// Distributed under the simplified BSD license (see the LICENSE file that
// should have come with this file).
// Benchmarks for moodycamel::ReaderWriterQueue.
#if defined(_MSC_VER) && _MSC_VER < 1700
#define NO_FOLLY_SUPPORT
#endif
#if defined(_MSC_VER) && _MSC_VER < 1700
#define NO_CIRCULAR_BUFFER_SUPPORT
#endif
#if !defined(__amd64__) && !defined(_M_X64) && !defined(__x86_64__) && !defined(_M_IX86) && !defined(__i386__)
#define NO_SPSC_SUPPORT // SPSC implementation is for x86 only
#endif
#include "ext/1024cores/spscqueue.h" // Dmitry's (on Intel site)
#ifndef NO_FOLLY_SUPPORT
#include "ext/folly/ProducerConsumerQueue.h" // Facebook's folly (GitHub)
#endif
#include "../readerwriterqueue.h" // Mine
#ifndef NO_CIRCULAR_BUFFER_SUPPORT
#include "../readerwritercircularbuffer.h" // Mine
template<typename T>
class BlockingReaderWriterCircularBufferAdapter : public moodycamel::BlockingReaderWriterCircularBuffer<T> {
public:
BlockingReaderWriterCircularBufferAdapter(std::size_t capacity) : moodycamel::BlockingReaderWriterCircularBuffer<T>(capacity) { }
void enqueue(T const& x) { this->wait_enqueue(x); }
};
#endif
#include "systemtime.h"
#include "../tests/common/simplethread.h"
#include <iostream>
#include <iomanip>
#include <numeric> // For std::accumulate
#include <algorithm>
#include <random>
#include <ctime>
#ifndef UNUSED
#define UNUSED(x) ((void)x);
#endif
using namespace moodycamel;
#ifndef NO_FOLLY_SUPPORT
using namespace folly;
#endif
typedef std::minstd_rand RNG_t;
enum BenchmarkType {
bench_raw_add,
bench_raw_remove,
bench_empty_remove,
bench_single_threaded,
bench_mostly_add,
bench_mostly_remove,
bench_heavy_concurrent,
bench_random_concurrent,
BENCHMARK_COUNT
};
// Returns the number of seconds elapsed (high-precision), and the number of enqueue/dequeue
// operations performed (in the out_Ops parameter)
template<typename TQueue>
double runBenchmark(BenchmarkType benchmark, unsigned int randomSeed, double& out_Ops);
const int BENCHMARK_NAME_MAX = 17; // Not including null terminator
const char* benchmarkName(BenchmarkType benchmark);
int main(int argc, char** argv)
{
#ifdef NDEBUG
const int TEST_COUNT = 25;
#else
const int TEST_COUNT = 2;
#endif
assert(TEST_COUNT >= 2);
const double FASTEST_PERCENT_CONSIDERED = 20; // Consider only the fastest runs in the top 20%
double rwqResults[BENCHMARK_COUNT][TEST_COUNT];
double brwcbResults[BENCHMARK_COUNT][TEST_COUNT];
double spscResults[BENCHMARK_COUNT][TEST_COUNT];
double follyResults[BENCHMARK_COUNT][TEST_COUNT];
// Also calculate a rough heuristic of "ops/s" (across all runs, not just fastest)
double rwqOps[BENCHMARK_COUNT][TEST_COUNT];
double brwcbOps[BENCHMARK_COUNT][TEST_COUNT];
double spscOps[BENCHMARK_COUNT][TEST_COUNT];
double follyOps[BENCHMARK_COUNT][TEST_COUNT];
// Make sure the randomness of each benchmark run is identical
unsigned int randSeeds[BENCHMARK_COUNT];
for (unsigned int i = 0; i != BENCHMARK_COUNT; ++i) {
randSeeds[i] = ((unsigned int)time(NULL)) * i;
}
// Run benchmarks
for (int benchmark = 0; benchmark < BENCHMARK_COUNT; ++benchmark) {
for (int i = 0; i < TEST_COUNT; ++i) {
rwqResults[benchmark][i] = runBenchmark<ReaderWriterQueue<int>>((BenchmarkType)benchmark, randSeeds[benchmark], rwqOps[benchmark][i]);
}
#ifndef NO_CIRCULAR_BUFFER_SUPPORT
for (int i = 0; i < TEST_COUNT; ++i) {
brwcbResults[benchmark][i] = runBenchmark<BlockingReaderWriterCircularBufferAdapter<int>>((BenchmarkType)benchmark, randSeeds[benchmark], brwcbOps[benchmark][i]);
}
#else
for (int i = 0; i < TEST_COUNT; ++i) {
brwcbResults[benchmark][i] = 0;
brwcbOps[benchmark][i] = 0;
}
#endif
#ifndef NO_SPSC_SUPPORT
for (int i = 0; i < TEST_COUNT; ++i) {
spscResults[benchmark][i] = runBenchmark<spsc_queue<int>>((BenchmarkType)benchmark, randSeeds[benchmark], spscOps[benchmark][i]);
}
#else
for (int i = 0; i < TEST_COUNT; ++i) {
spscResults[benchmark][i] = 0;
spscOps[benchmark][i] = 0;
}
#endif
#ifndef NO_FOLLY_SUPPORT
for (int i = 0; i < TEST_COUNT; ++i) {
follyResults[benchmark][i] = runBenchmark<ProducerConsumerQueue<int>>((BenchmarkType)benchmark, randSeeds[benchmark], follyOps[benchmark][i]);
}
#else
for (int i = 0; i < TEST_COUNT; ++i) {
follyResults[benchmark][i] = 0;
follyOps[benchmark][i] = 0;
}
#endif
}
// Sort results
for (int benchmark = 0; benchmark < BENCHMARK_COUNT; ++benchmark) {
std::sort(&rwqResults[benchmark][0], &rwqResults[benchmark][0] + TEST_COUNT);
std::sort(&brwcbResults[benchmark][0], &brwcbResults[benchmark][0] + TEST_COUNT);
std::sort(&spscResults[benchmark][0], &spscResults[benchmark][0] + TEST_COUNT);
std::sort(&follyResults[benchmark][0], &follyResults[benchmark][0] + TEST_COUNT);
}
// Display results
int max = std::max(2, (int)(TEST_COUNT * FASTEST_PERCENT_CONSIDERED / 100));
assert(max > 0);
#ifdef NO_CIRCULAR_BUFFER_SUPPORT
std::cout << "Note: BRWCB queue not supported on this platform, discount its timings" << std::endl;
#endif
#ifdef NO_SPSC_SUPPORT
std::cout << "Note: SPSC queue not supported on this platform, discount its timings" << std::endl;
#endif
#ifdef NO_FOLLY_SUPPORT
std::cout << "Note: Folly queue not supported by this compiler, discount its timings" << std::endl;
#endif
std::cout << std::setw(BENCHMARK_NAME_MAX) << " " << " |---------------- Min -----------------|----------------- Max -----------------|----------------- Avg -----------------|\n";
std::cout << std::left << std::setw(BENCHMARK_NAME_MAX) << "Benchmark" << " | RWQ | BRWCB | SPSC | Folly | RWQ | BRWCB | SPSC | Folly | RWQ | BRWCB | SPSC | Folly | xSPSC | xFolly\n";
std::cout.fill('-');
std::cout << std::setw(BENCHMARK_NAME_MAX) << "---------" << "-+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+---------+-------+-------\n";
std::cout.fill(' ');
double rwqOpsPerSec = 0, brwcbOpsPerSec = 0, spscOpsPerSec = 0, follyOpsPerSec = 0;
int opTimedBenchmarks = 0;
for (int benchmark = 0; benchmark < BENCHMARK_COUNT; ++benchmark) {
double rwqMin = rwqResults[benchmark][0], rwqMax = rwqResults[benchmark][max - 1];
double brwcbMin = brwcbResults[benchmark][0], brwcbMax = brwcbResults[benchmark][max - 1];
double spscMin = spscResults[benchmark][0], spscMax = spscResults[benchmark][max - 1];
double follyMin = follyResults[benchmark][0], follyMax = follyResults[benchmark][max - 1];
double rwqAvg = std::accumulate(&rwqResults[benchmark][0], &rwqResults[benchmark][0] + max, 0.0) / max;
double brwcbAvg = std::accumulate(&brwcbResults[benchmark][0], &brwcbResults[benchmark][0] + max, 0.0) / max;
double spscAvg = std::accumulate(&spscResults[benchmark][0], &spscResults[benchmark][0] + max, 0.0) / max;
double follyAvg = std::accumulate(&follyResults[benchmark][0], &follyResults[benchmark][0] + max, 0.0) / max;
double spscMult = rwqAvg < 0.00001 ? 0 : spscAvg / rwqAvg;
double follyMult = follyAvg < 0.00001 ? 0 : follyAvg / rwqAvg;
if (rwqResults[benchmark][0] != -1) {
double rwqTotalAvg = std::accumulate(&rwqResults[benchmark][0], &rwqResults[benchmark][0] + TEST_COUNT, 0.0) / TEST_COUNT;
double brwcbTotalAvg = std::accumulate(&brwcbResults[benchmark][0], &brwcbResults[benchmark][0] + TEST_COUNT, 0.0) / TEST_COUNT;
double spscTotalAvg = std::accumulate(&spscResults[benchmark][0], &spscResults[benchmark][0] + TEST_COUNT, 0.0) / TEST_COUNT;
double follyTotalAvg = std::accumulate(&follyResults[benchmark][0], &follyResults[benchmark][0] + TEST_COUNT, 0.0) / TEST_COUNT;
rwqOpsPerSec += rwqTotalAvg == 0 ? 0 : std::accumulate(&rwqOps[benchmark][0], &rwqOps[benchmark][0] + TEST_COUNT, 0.0) / TEST_COUNT / rwqTotalAvg;
brwcbOpsPerSec += brwcbTotalAvg == 0 ? 0 : std::accumulate(&brwcbOps[benchmark][0], &brwcbOps[benchmark][0] + TEST_COUNT, 0.0) / TEST_COUNT / brwcbTotalAvg;
spscOpsPerSec += spscTotalAvg == 0 ? 0 : std::accumulate(&spscOps[benchmark][0], &spscOps[benchmark][0] + TEST_COUNT, 0.0) / TEST_COUNT / spscTotalAvg;
follyOpsPerSec += follyTotalAvg == 0 ? 0 : std::accumulate(&follyOps[benchmark][0], &follyOps[benchmark][0] + TEST_COUNT, 0.0) / TEST_COUNT / follyTotalAvg;
++opTimedBenchmarks;
}
std::cout
<< std::left << std::setw(BENCHMARK_NAME_MAX) << benchmarkName((BenchmarkType)benchmark) << " | "
<< std::fixed << std::setprecision(4) << rwqMin << "s | "
<< std::fixed << std::setprecision(4) << brwcbMin << "s | "
<< std::fixed << std::setprecision(4) << spscMin << "s | "
<< std::fixed << std::setprecision(4) << follyMin << "s | "
<< std::fixed << std::setprecision(4) << rwqMax << "s | "
<< std::fixed << std::setprecision(4) << brwcbMax << "s | "
<< std::fixed << std::setprecision(4) << spscMax << "s | "
<< std::fixed << std::setprecision(4) << follyMax << "s | "
<< std::fixed << std::setprecision(4) << rwqAvg << "s | "
<< std::fixed << std::setprecision(4) << brwcbAvg << "s | "
<< std::fixed << std::setprecision(4) << spscAvg << "s | "
<< std::fixed << std::setprecision(4) << follyAvg << "s | "
<< std::fixed << std::setprecision(2) << spscMult << "x | "
<< std::fixed << std::setprecision(2) << follyMult << "x"
<< "\n"
;
}
rwqOpsPerSec /= opTimedBenchmarks;
brwcbOpsPerSec /= opTimedBenchmarks;
spscOpsPerSec /= opTimedBenchmarks;
follyOpsPerSec /= opTimedBenchmarks;
std::cout
<< "\nAverage ops/s:\n"
<< " ReaderWriterQueue: " << std::fixed << std::setprecision(2) << rwqOpsPerSec / 1000000 << " million\n"
<< " BlockingReaderWriterCircularBuffer: " << std::fixed << std::setprecision(2) << brwcbOpsPerSec / 1000000 << " million\n"
<< " SPSC queue: " << std::fixed << std::setprecision(2) << spscOpsPerSec / 1000000 << " million\n"
<< " Folly queue: " << std::fixed << std::setprecision(2) << follyOpsPerSec / 1000000 << " million\n"
;
std::cout << std::endl;
return 0;
}
template<typename TQueue>
double runBenchmark(BenchmarkType benchmark, unsigned int randomSeed, double& out_Ops)
{
typedef unsigned long long counter_t;
SystemTime start;
double result = 0;
volatile int forceNoOptimizeDummy;
switch (benchmark) {
case bench_raw_add: {
const counter_t MAX = 100 * 1000;
out_Ops = MAX;
TQueue q(MAX);
int num = 0;
start = getSystemTime();
for (counter_t i = 0; i != MAX; ++i) {
q.enqueue(num);
++num;
}
result = getTimeDelta(start);
int temp = -1;
q.try_dequeue(temp);
forceNoOptimizeDummy = temp;
} break;
case bench_raw_remove: {
const counter_t MAX = 100 * 1000;
out_Ops = MAX;
TQueue q(MAX);
int num = 0;
for (counter_t i = 0; i != MAX; ++i) {
q.enqueue(num);
++num;
}
int element = -1;
int total = 0;
num = 0;
start = getSystemTime();
for (counter_t i = 0; i != MAX; ++i) {
bool success = q.try_dequeue(element);
assert(success && num++ == element);
UNUSED(success);
total += element;
}
result = getTimeDelta(start);
assert(!q.try_dequeue(element));
forceNoOptimizeDummy = total;
} break;
case bench_empty_remove: {
const counter_t MAX = 2000 * 1000;
out_Ops = MAX;
TQueue q(MAX);
int total = 0;
start = getSystemTime();
SimpleThread consumer([&]() {
int element;
for (counter_t i = 0; i != MAX; ++i) {
if (q.try_dequeue(element)) {
total += element;
}
}
});
SimpleThread producer([&]() {
int num = 0;
for (counter_t i = 0; i != MAX / 2; ++i) {
if ((i & 32767) == 0) { // Just to make sure the loops aren't optimized out entirely
q.enqueue(num);
++num;
}
}
});
producer.join();
consumer.join();
result = getTimeDelta(start);
forceNoOptimizeDummy = total;
} break;
case bench_single_threaded: {
const counter_t MAX = 200 * 1000;
out_Ops = MAX;
RNG_t rng(randomSeed);
std::uniform_int_distribution<int> rand(0, 1);
TQueue q(MAX);
int num = 0;
int element = -1;
start = getSystemTime();
for (counter_t i = 0; i != MAX; ++i) {
if (rand(rng) == 1) {
q.enqueue(num);
++num;
}
else {
q.try_dequeue(element);
}
}
result = getTimeDelta(start);
forceNoOptimizeDummy = (int)(q.try_dequeue(element));
} break;
case bench_mostly_add: {
const counter_t MAX = 1200 * 1000;
out_Ops = MAX;
int readOps = 0;
RNG_t rng(randomSeed);
std::uniform_int_distribution<int> rand(0, 3);
TQueue q(MAX);
int element = -1;
start = getSystemTime();
SimpleThread consumer([&]() {
for (counter_t i = 0; i != MAX / 10; ++i) {
if (rand(rng) == 0) {
q.try_dequeue(element);
++readOps;
}
}
});
SimpleThread producer([&]() {
int num = 0;
for (counter_t i = 0; i != MAX; ++i) {
q.enqueue(num);
++num;
}
});
producer.join();
consumer.join();
result = getTimeDelta(start);
forceNoOptimizeDummy = (int)(q.try_dequeue(element));
out_Ops += readOps;
} break;
case bench_mostly_remove: {
const counter_t MAX = 1200 * 1000;
out_Ops = MAX;
int writeOps = 0;
RNG_t rng(randomSeed);
std::uniform_int_distribution<int> rand(0, 3);
TQueue q(MAX);
int element = -1;
start = getSystemTime();
SimpleThread consumer([&]() {
for (counter_t i = 0; i != MAX; ++i) {
q.try_dequeue(element);
}
});
SimpleThread producer([&]() {
int num = 0;
for (counter_t i = 0; i != MAX / 10; ++i) {
if (rand(rng) == 0) {
q.enqueue(num);
++num;
}
}
writeOps = num;
});
producer.join();
consumer.join();
result = getTimeDelta(start);
forceNoOptimizeDummy = (int)(q.try_dequeue(element));
out_Ops += writeOps;
} break;
case bench_heavy_concurrent: {
const counter_t MAX = 1000 * 1000;
out_Ops = MAX * 2;
TQueue q(MAX);
int element = -1;
start = getSystemTime();
SimpleThread consumer([&]() {
for (counter_t i = 0; i != MAX; ++i) {
q.try_dequeue(element);
}
});
SimpleThread producer([&]() {
int num = 0;
for (counter_t i = 0; i != MAX; ++i) {
q.enqueue(num);
++num;
}
});
producer.join();
consumer.join();
result = getTimeDelta(start);
forceNoOptimizeDummy = (int)(q.try_dequeue(element));
} break;
case bench_random_concurrent: {
const counter_t MAX = 800 * 1000;
int readOps = 0, writeOps = 0;
TQueue q(MAX);
int element = -1;
start = getSystemTime();
SimpleThread consumer([&]() {
RNG_t rng(randomSeed);
std::uniform_int_distribution<int> rand(0, 15);
for (counter_t i = 0; i != MAX; ++i) {
if (rand(rng) == 0) {
q.try_dequeue(element);
++readOps;
}
}
});
SimpleThread producer([&]() {
RNG_t rng(randomSeed * 3 - 1);
std::uniform_int_distribution<int> rand(0, 15);
int num = 0;
for (counter_t i = 0; i != MAX; ++i) {
if (rand(rng) == 0) {
q.enqueue(num);
++num;
}
}
writeOps = num;
});
producer.join();
consumer.join();
result = getTimeDelta(start);
forceNoOptimizeDummy = (int)(q.try_dequeue(element));
out_Ops = readOps + writeOps;
} break;
default:
assert(false);
out_Ops = 0;
return 0;
}
UNUSED(forceNoOptimizeDummy);
return result / 1000.0;
}
const char* benchmarkName(BenchmarkType benchmark)
{
switch (benchmark) {
case bench_raw_add: return "Raw add";
case bench_raw_remove: return "Raw remove";
case bench_empty_remove: return "Raw empty remove";
case bench_single_threaded: return "Single-threaded";
case bench_mostly_add: return "Mostly add";
case bench_mostly_remove: return "Mostly remove";
case bench_heavy_concurrent: return "Heavy concurrent";
case bench_random_concurrent: return "Random concurrent";
default: return "";
}
}

View file

@ -0,0 +1,139 @@
#include "../../../atomicops.h"
#include <cstdlib> // For std::size_t
// From http://www.1024cores.net/home/lock-free-algorithms/queues/unbounded-spsc-queue
// (and http://software.intel.com/en-us/articles/single-producer-single-consumer-queue)
// load with 'consume' (data-dependent) memory ordering
template<typename T>
T load_consume(T const* addr)
{
// hardware fence is implicit on x86
T v = *const_cast<T const volatile*>(addr);
moodycamel::compiler_fence(moodycamel::memory_order_seq_cst);
return v;
}
// store with 'release' memory ordering
template<typename T>
void store_release(T* addr, T v)
{
// hardware fence is implicit on x86
moodycamel::compiler_fence(moodycamel::memory_order_seq_cst);
*const_cast<T volatile*>(addr) = v;
}
// cache line size on modern x86 processors (in bytes)
size_t const cache_line_size = 64;
// single-producer/single-consumer queue
template<typename T>
class spsc_queue
{
public:
spsc_queue()
{
node* n = new node;
n->next_ = 0;
tail_ = head_ = first_= tail_copy_ = n;
}
explicit spsc_queue(size_t prealloc)
{
node* n = new node;
n->next_ = 0;
tail_ = head_ = first_ = tail_copy_ = n;
// [CD] Not (at all) the most efficient way to pre-allocate memory, but it works
T dummy = T();
for (size_t i = 0; i != prealloc; ++i) {
enqueue(dummy);
}
for (size_t i = 0; i != prealloc; ++i) {
try_dequeue(dummy);
}
}
~spsc_queue()
{
node* n = first_;
do
{
node* next = n->next_;
delete n;
n = next;
}
while (n);
}
void enqueue(T v)
{
node* n = alloc_node();
n->next_ = 0;
n->value_ = v;
store_release(&head_->next_, n);
head_ = n;
}
// returns 'false' if queue is empty
bool try_dequeue(T& v)
{
if (load_consume(&tail_->next_))
{
v = tail_->next_->value_;
store_release(&tail_, tail_->next_);
return true;
}
else
{
return false;
}
}
private:
// internal node structure
struct node
{
node* next_;
T value_;
};
// consumer part
// accessed mainly by consumer, infrequently be producer
node* tail_; // tail of the queue
// delimiter between consumer part and producer part,
// so that they situated on different cache lines
char cache_line_pad_ [cache_line_size];
// producer part
// accessed only by producer
node* head_; // head of the queue
node* first_; // last unused node (tail of node cache)
node* tail_copy_; // helper (points somewhere between first_ and tail_)
node* alloc_node()
{
// first tries to allocate node from internal node cache,
// if attempt fails, allocates node via ::operator new()
if (first_ != tail_copy_)
{
node* n = first_;
first_ = first_->next_;
return n;
}
tail_copy_ = load_consume(&tail_);
if (first_ != tail_copy_)
{
node* n = first_;
first_ = first_->next_;
return n;
}
node* n = new node;
return n;
}
spsc_queue(spsc_queue const&);
spsc_queue& operator = (spsc_queue const&);
};

View file

@ -0,0 +1,174 @@
// Adapted from https://github.com/facebook/folly/blob/master/folly/ProducerConsumerQueue.h
/*
* Copyright 2013 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// @author Bo Hu (bhu@fb.com)
// @author Jordan DeLong (delong.j@fb.com)
#ifndef PRODUCER_CONSUMER_QUEUE_H_
#define PRODUCER_CONSUMER_QUEUE_H_
#include <new>
#include <atomic>
#include <cassert>
#include <cstdlib>
#include <stdexcept>
#include <type_traits>
#include <utility>
//#include <boost/noncopyable.hpp>
namespace folly {
/*
* ProducerConsumerQueue is a one producer and one consumer queue
* without locks.
*/
template<class T>
struct ProducerConsumerQueue {
typedef T value_type;
// size must be >= 1.
explicit ProducerConsumerQueue(uint32_t size)
: size_(size + 1) // +1 because one slot is always empty
, records_(static_cast<T*>(std::malloc(sizeof(T) * (size + 1))))
, readIndex_(0)
, writeIndex_(0)
{
assert(size >= 1);
if (!records_) {
throw std::bad_alloc();
}
}
~ProducerConsumerQueue() {
// We need to destruct anything that may still exist in our queue.
// (No real synchronization needed at destructor time: only one
// thread can be doing this.)
if (!std::is_trivially_destructible<T>::value) {
int read = readIndex_;
int end = writeIndex_;
while (read != end) {
records_[read].~T();
if (++read == size_) {
read = 0;
}
}
}
std::free(records_);
}
template<class ...Args>
bool enqueue(Args&&... recordArgs) {
auto const currentWrite = writeIndex_.load(std::memory_order_relaxed);
auto nextRecord = currentWrite + 1;
if (nextRecord == size_) {
nextRecord = 0;
}
if (nextRecord != readIndex_.load(std::memory_order_acquire)) {
new (&records_[currentWrite]) T(std::forward<Args>(recordArgs)...);
writeIndex_.store(nextRecord, std::memory_order_release);
return true;
}
// queue is full
return false;
}
// move (or copy) the value at the front of the queue to given variable
bool try_dequeue(T& record) {
auto const currentRead = readIndex_.load(std::memory_order_relaxed);
if (currentRead == writeIndex_.load(std::memory_order_acquire)) {
// queue is empty
return false;
}
auto nextRecord = currentRead + 1;
if (nextRecord == size_) {
nextRecord = 0;
}
record = std::move(records_[currentRead]);
records_[currentRead].~T();
readIndex_.store(nextRecord, std::memory_order_release);
return true;
}
// pointer to the value at the front of the queue (for use in-place) or
// nullptr if empty.
T* frontPtr() {
auto const currentRead = readIndex_.load(std::memory_order_relaxed);
if (currentRead == writeIndex_.load(std::memory_order_acquire)) {
// queue is empty
return nullptr;
}
return &records_[currentRead];
}
// queue must not be empty
void popFront() {
auto const currentRead = readIndex_.load(std::memory_order_relaxed);
assert(currentRead != writeIndex_.load(std::memory_order_acquire));
auto nextRecord = currentRead + 1;
if (nextRecord == size_) {
nextRecord = 0;
}
records_[currentRead].~T();
readIndex_.store(nextRecord, std::memory_order_release);
}
bool isEmpty() const {
return readIndex_.load(std::memory_order_consume) ==
writeIndex_.load(std::memory_order_consume);
}
bool isFull() const {
auto nextRecord = writeIndex_.load(std::memory_order_consume) + 1;
if (nextRecord == size_) {
nextRecord = 0;
}
if (nextRecord != readIndex_.load(std::memory_order_consume)) {
return false;
}
// queue is full
return true;
}
// * If called by consumer, then true size may be more (because producer may
// be adding items concurrently).
// * If called by producer, then true size may be less (because consumer may
// be removing items concurrently).
// * It is undefined to call this from any other thread.
size_t sizeGuess() const {
int ret = writeIndex_.load(std::memory_order_consume) -
readIndex_.load(std::memory_order_consume);
if (ret < 0) {
ret += size_;
}
return ret;
}
private:
const uint32_t size_;
T* const records_;
std::atomic<int> readIndex_;
std::atomic<int> writeIndex_;
};
}
#endif

View file

@ -0,0 +1,22 @@
# ©2014 Cameron Desrochers
ifeq ($(OS),Windows_NT)
EXT=.exe
PLATFORM_OPTS=-static
else
EXT=
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S),Darwin)
PLATFORM_OPTS=
else
PLATFORM_OPTS=-Wl,--no-as-needed -lrt
endif
endif
default: benchmarks$(EXT)
benchmarks$(EXT): bench.cpp ../readerwriterqueue.h ../readerwritercircularbuffer.h ../atomicops.h ext/1024cores/spscqueue.h ext/folly/ProducerConsumerQueue.h ../tests/common/simplethread.h ../tests/common/simplethread.cpp systemtime.h systemtime.cpp makefile
g++ -std=c++11 -Wpedantic -Wall -DNDEBUG -O3 -g bench.cpp ../tests/common/simplethread.cpp systemtime.cpp -o benchmarks$(EXT) -pthread $(PLATFORM_OPTS)
run: benchmarks$(EXT)
./benchmarks$(EXT)

View file

@ -0,0 +1,165 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>winbenchintel</RootNamespace>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>Intel C++ Compiler XE 13.0</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>Intel C++ Compiler XE 13.0</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>Intel C++ Compiler XE 13.0</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>Intel C++ Compiler XE 13.0</PlatformToolset>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)\</OutDir>
<IntDir>obj\$(Configuration)\$(Platform)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)\</OutDir>
<IntDir>obj\$(Configuration)\$(Platform)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)\</OutDir>
<IntDir>obj\$(Configuration)\$(Platform)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)\</OutDir>
<IntDir>obj\$(Configuration)\$(Platform)\</IntDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\..\tests\common\simplethread.cpp" />
<ClCompile Include="..\systemtime.cpp" />
<ClCompile Include="..\bench.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\atomicops.h" />
<ClInclude Include="..\..\readerwriterqueue.h" />
<ClInclude Include="..\..\tests\common\simplethread.h" />
<ClInclude Include="..\ext\1024cores\spscqueue.h" />
<ClInclude Include="..\ext\folly\ProducerConsumerQueue.h" />
<ClInclude Include="..\systemtime.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View file

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="atomicops.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="readerwriterqueue.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="simplethread.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="spscqueue.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="systemtime.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="bench.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="simplethread.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="systemtime.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

View file

@ -0,0 +1,34 @@

Microsoft Visual Studio Solution File, Format Version 11.00
# Visual Studio 2010
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "winbench", "winbench.vcxproj", "{E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "winbench-intel", "winbench-intel.vcxproj", "{6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Debug|x64 = Debug|x64
Release|Win32 = Release|Win32
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Debug|Win32.ActiveCfg = Debug|Win32
{E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Debug|Win32.Build.0 = Debug|Win32
{E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Debug|x64.ActiveCfg = Debug|Win32
{E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Debug|x64.Build.0 = Debug|Win32
{E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Release|Win32.ActiveCfg = Release|Win32
{E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Release|Win32.Build.0 = Release|Win32
{E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Release|x64.ActiveCfg = Release|x64
{E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Release|x64.Build.0 = Release|x64
{6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Debug|Win32.ActiveCfg = Debug|Win32
{6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Debug|Win32.Build.0 = Debug|Win32
{6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Debug|x64.ActiveCfg = Debug|Win32
{6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Release|Win32.ActiveCfg = Release|Win32
{6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Release|Win32.Build.0 = Release|Win32
{6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Release|x64.ActiveCfg = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View file

@ -0,0 +1,161 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>winbench</RootNamespace>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)\</OutDir>
<IntDir>obj\$(Configuration)\$(Platform)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)\</OutDir>
<IntDir>obj\$(Configuration)\$(Platform)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)\</OutDir>
<IntDir>obj\$(Configuration)\$(Platform)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)\</OutDir>
<IntDir>obj\$(Configuration)\$(Platform)\</IntDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\..\tests\common\simplethread.cpp" />
<ClCompile Include="..\systemtime.cpp" />
<ClCompile Include="..\bench.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\atomicops.h" />
<ClInclude Include="..\..\readerwriterqueue.h" />
<ClInclude Include="..\..\tests\common\simplethread.h" />
<ClInclude Include="..\ext\1024cores\spscqueue.h" />
<ClInclude Include="..\ext\folly\ProducerConsumerQueue.h" />
<ClInclude Include="..\systemtime.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View file

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="systemtime.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="bench.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="simplethread.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="atomicops.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="readerwriterqueue.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="spscqueue.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="systemtime.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="simplethread.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>

View file

@ -0,0 +1,165 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>winbenchintel</RootNamespace>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>Intel C++ Compiler XE 13.0</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>Intel C++ Compiler XE 13.0</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>Intel C++ Compiler XE 13.0</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>Intel C++ Compiler XE 13.0</PlatformToolset>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)\</OutDir>
<IntDir>obj\$(Configuration)\$(Platform)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)\</OutDir>
<IntDir>obj\$(Configuration)\$(Platform)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)\</OutDir>
<IntDir>obj\$(Configuration)\$(Platform)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)\</OutDir>
<IntDir>obj\$(Configuration)\$(Platform)\</IntDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\..\tests\common\simplethread.cpp" />
<ClCompile Include="..\systemtime.cpp" />
<ClCompile Include="..\bench.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\atomicops.h" />
<ClInclude Include="..\..\readerwriterqueue.h" />
<ClInclude Include="..\..\tests\common\simplethread.h" />
<ClInclude Include="..\ext\1024cores\spscqueue.h" />
<ClInclude Include="..\ext\folly\ProducerConsumerQueue.h" />
<ClInclude Include="..\systemtime.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View file

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="atomicops.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="readerwriterqueue.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="simplethread.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="spscqueue.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="systemtime.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="bench.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="simplethread.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="systemtime.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

View file

@ -0,0 +1,36 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2013
VisualStudioVersion = 12.0.30501.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "winbench", "winbench.vcxproj", "{E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "winbench-intel", "winbench-intel.vcxproj", "{6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Debug|x64 = Debug|x64
Release|Win32 = Release|Win32
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Debug|Win32.ActiveCfg = Debug|Win32
{E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Debug|Win32.Build.0 = Debug|Win32
{E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Debug|x64.ActiveCfg = Debug|Win32
{E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Debug|x64.Build.0 = Debug|Win32
{E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Release|Win32.ActiveCfg = Release|Win32
{E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Release|Win32.Build.0 = Release|Win32
{E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Release|x64.ActiveCfg = Release|x64
{E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Release|x64.Build.0 = Release|x64
{6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Debug|Win32.ActiveCfg = Debug|Win32
{6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Debug|Win32.Build.0 = Debug|Win32
{6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Debug|x64.ActiveCfg = Debug|Win32
{6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Release|Win32.ActiveCfg = Release|Win32
{6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Release|Win32.Build.0 = Release|Win32
{6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Release|x64.ActiveCfg = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View file

@ -0,0 +1,165 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>winbench</RootNamespace>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>v120</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>v120</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>v120</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>v120</PlatformToolset>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)\</OutDir>
<IntDir>obj\$(Configuration)\$(Platform)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)\</OutDir>
<IntDir>obj\$(Configuration)\$(Platform)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)\</OutDir>
<IntDir>obj\$(Configuration)\$(Platform)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)\</OutDir>
<IntDir>obj\$(Configuration)\$(Platform)\</IntDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\..\tests\common\simplethread.cpp" />
<ClCompile Include="..\systemtime.cpp" />
<ClCompile Include="..\bench.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\atomicops.h" />
<ClInclude Include="..\..\readerwriterqueue.h" />
<ClInclude Include="..\..\tests\common\simplethread.h" />
<ClInclude Include="..\ext\1024cores\spscqueue.h" />
<ClInclude Include="..\ext\folly\ProducerConsumerQueue.h" />
<ClInclude Include="..\systemtime.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View file

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\tests\common\simplethread.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\systemtime.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\bench.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\atomicops.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\readerwriterqueue.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\tests\common\simplethread.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\ext\1024cores\spscqueue.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\ext\folly\ProducerConsumerQueue.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\systemtime.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>

View file

@ -0,0 +1,165 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>winbenchintel</RootNamespace>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>Intel C++ Compiler XE 13.0</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>Intel C++ Compiler XE 13.0</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>Intel C++ Compiler XE 13.0</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>Intel C++ Compiler XE 13.0</PlatformToolset>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)\</OutDir>
<IntDir>obj\$(Configuration)\$(Platform)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)\</OutDir>
<IntDir>obj\$(Configuration)\$(Platform)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)\</OutDir>
<IntDir>obj\$(Configuration)\$(Platform)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)\</OutDir>
<IntDir>obj\$(Configuration)\$(Platform)\</IntDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\..\tests\common\simplethread.cpp" />
<ClCompile Include="..\systemtime.cpp" />
<ClCompile Include="..\bench.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\atomicops.h" />
<ClInclude Include="..\..\readerwriterqueue.h" />
<ClInclude Include="..\..\tests\common\simplethread.h" />
<ClInclude Include="..\ext\1024cores\spscqueue.h" />
<ClInclude Include="..\ext\folly\ProducerConsumerQueue.h" />
<ClInclude Include="..\systemtime.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View file

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="atomicops.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="readerwriterqueue.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="simplethread.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="spscqueue.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="systemtime.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="bench.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="simplethread.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="systemtime.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

View file

@ -0,0 +1,36 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.25420.1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "winbench", "winbench.vcxproj", "{E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "winbench-intel", "winbench-intel.vcxproj", "{6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Debug|x64 = Debug|x64
Release|Win32 = Release|Win32
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Debug|Win32.ActiveCfg = Debug|Win32
{E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Debug|Win32.Build.0 = Debug|Win32
{E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Debug|x64.ActiveCfg = Debug|Win32
{E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Debug|x64.Build.0 = Debug|Win32
{E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Release|Win32.ActiveCfg = Release|Win32
{E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Release|Win32.Build.0 = Release|Win32
{E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Release|x64.ActiveCfg = Release|x64
{E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}.Release|x64.Build.0 = Release|x64
{6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Debug|Win32.ActiveCfg = Debug|Win32
{6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Debug|Win32.Build.0 = Debug|Win32
{6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Debug|x64.ActiveCfg = Debug|Win32
{6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Release|Win32.ActiveCfg = Release|Win32
{6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Release|Win32.Build.0 = Release|Win32
{6B5A3DA6-68D1-46B9-B86C-D02236EABBC9}.Release|x64.ActiveCfg = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View file

@ -0,0 +1,165 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{E58A7EAF-6162-41F1-AD5C-7DD71FB7ABE2}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>winbench</RootNamespace>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>v140</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>v140</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>v140</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>v140</PlatformToolset>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)\</OutDir>
<IntDir>obj\$(Configuration)\$(Platform)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)\</OutDir>
<IntDir>obj\$(Configuration)\$(Platform)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)\</OutDir>
<IntDir>obj\$(Configuration)\$(Platform)\</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)\</OutDir>
<IntDir>obj\$(Configuration)\$(Platform)\</IntDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\..\tests\common\simplethread.cpp" />
<ClCompile Include="..\systemtime.cpp" />
<ClCompile Include="..\bench.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\atomicops.h" />
<ClInclude Include="..\..\readerwriterqueue.h" />
<ClInclude Include="..\..\tests\common\simplethread.h" />
<ClInclude Include="..\ext\1024cores\spscqueue.h" />
<ClInclude Include="..\ext\folly\ProducerConsumerQueue.h" />
<ClInclude Include="..\systemtime.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View file

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\tests\common\simplethread.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\systemtime.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\bench.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\atomicops.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\readerwriterqueue.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\tests\common\simplethread.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\ext\1024cores\spscqueue.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\ext\folly\ProducerConsumerQueue.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\systemtime.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>

View file

@ -0,0 +1,137 @@
// ©2013-2014 Cameron Desrochers
#include "systemtime.h"
#include <climits>
#if defined(_MSC_VER) && _MSC_VER < 1700
#include <intrin.h>
#define CompilerMemBar() _ReadWriteBarrier()
#else
#include <atomic>
#define CompilerMemBar() std::atomic_signal_fence(std::memory_order_seq_cst)
#endif
#if defined(ST_WINDOWS)
#include <windows.h>
namespace moodycamel
{
void sleep(int milliseconds)
{
::Sleep(milliseconds);
}
SystemTime getSystemTime()
{
LARGE_INTEGER t;
CompilerMemBar();
if (!QueryPerformanceCounter(&t)) {
return static_cast<SystemTime>(-1);
}
CompilerMemBar();
return static_cast<SystemTime>(t.QuadPart);
}
double getTimeDelta(SystemTime start)
{
LARGE_INTEGER t;
CompilerMemBar();
if (start == static_cast<SystemTime>(-1) || !QueryPerformanceCounter(&t)) {
return -1;
}
CompilerMemBar();
auto now = static_cast<SystemTime>(t.QuadPart);
LARGE_INTEGER f;
if (!QueryPerformanceFrequency(&f)) {
return -1;
}
return static_cast<double>(static_cast<__int64>(now - start)) / f.QuadPart * 1000;
}
} // end namespace moodycamel
#elif defined(ST_APPLE)
#include <mach/mach.h>
#include <mach/mach_time.h>
#include <unistd.h>
#include <time.h>
namespace moodycamel
{
void sleep(int milliseconds)
{
::usleep(milliseconds * 1000);
}
SystemTime getSystemTime()
{
CompilerMemBar();
std::uint64_t result = mach_absolute_time();
CompilerMemBar();
return result;
}
double getTimeDelta(SystemTime start)
{
CompilerMemBar();
std::uint64_t end = mach_absolute_time();
CompilerMemBar();
mach_timebase_info_data_t tb = { 0 };
mach_timebase_info(&tb);
double toNano = static_cast<double>(tb.numer) / tb.denom;
return static_cast<double>(end - start) * toNano * 0.000001;
}
} // end namespace moodycamel
#elif defined(ST_NIX)
#include <unistd.h>
namespace moodycamel
{
void sleep(int milliseconds)
{
::usleep(milliseconds * 1000);
}
SystemTime getSystemTime()
{
timespec t;
CompilerMemBar();
if (clock_gettime(CLOCK_MONOTONIC_RAW, &t) != 0) {
t.tv_sec = (time_t)-1;
t.tv_nsec = -1;
}
CompilerMemBar();
return t;
}
double getTimeDelta(SystemTime start)
{
timespec t;
CompilerMemBar();
if ((start.tv_sec == (time_t)-1 && start.tv_nsec == -1) || clock_gettime(CLOCK_MONOTONIC_RAW, &t) != 0) {
return -1;
}
CompilerMemBar();
return static_cast<double>(static_cast<long>(t.tv_sec) - static_cast<long>(start.tv_sec)) * 1000 + double(t.tv_nsec - start.tv_nsec) / 1000000;
}
} // end namespace moodycamel
#endif

View file

@ -0,0 +1,33 @@
// ©2013-2014 Cameron Desrochers
#pragma once
#if defined(_WIN32)
#define ST_WINDOWS
#elif defined(__APPLE__) && defined(__MACH__)
#define ST_APPLE
#elif defined(__linux__) || defined(__FreeBSD__) || defined(BSD)
#define ST_NIX
#else
#error "Unknown platform"
#endif
#if defined(ST_WINDOWS)
namespace moodycamel { typedef unsigned long long SystemTime; }
#elif defined(ST_APPLE)
#include <cstdint>
namespace moodycamel { typedef std::uint64_t SystemTime; }
#elif defined(ST_NIX)
#include <time.h>
namespace moodycamel { typedef timespec SystemTime; }
#endif
namespace moodycamel
{
void sleep(int milliseconds);
SystemTime getSystemTime();
// Returns the delta time, in milliseconds
double getTimeDelta(SystemTime start);
}

View file

@ -0,0 +1,321 @@
// ©2020 Cameron Desrochers.
// Distributed under the simplified BSD license (see the license file that
// should have come with this header).
// Provides a C++11 implementation of a single-producer, single-consumer wait-free concurrent
// circular buffer (fixed-size queue).
#pragma once
#include <utility>
#include <chrono>
#include <memory>
#include <cstdlib>
#include <cstdint>
#include <cassert>
// Note that this implementation is fully modern C++11 (not compatible with old MSVC versions)
// but we still include atomicops.h for its LightweightSemaphore implementation.
#include "atomicops.h"
#ifndef MOODYCAMEL_CACHE_LINE_SIZE
#define MOODYCAMEL_CACHE_LINE_SIZE 64
#endif
namespace moodycamel {
template<typename T>
class BlockingReaderWriterCircularBuffer
{
public:
typedef T value_type;
public:
explicit BlockingReaderWriterCircularBuffer(std::size_t capacity)
: maxcap(capacity), mask(), rawData(), data(),
slots_(new spsc_sema::LightweightSemaphore(static_cast<spsc_sema::LightweightSemaphore::ssize_t>(capacity))),
items(new spsc_sema::LightweightSemaphore(0)),
nextSlot(0), nextItem(0)
{
// Round capacity up to power of two to compute modulo mask.
// Adapted from http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
--capacity;
capacity |= capacity >> 1;
capacity |= capacity >> 2;
capacity |= capacity >> 4;
for (std::size_t i = 1; i < sizeof(std::size_t); i <<= 1)
capacity |= capacity >> (i << 3);
mask = capacity++;
rawData = static_cast<char*>(std::malloc(capacity * sizeof(T) + std::alignment_of<T>::value - 1));
data = align_for<T>(rawData);
}
BlockingReaderWriterCircularBuffer(BlockingReaderWriterCircularBuffer&& other)
: maxcap(0), mask(0), rawData(nullptr), data(nullptr),
slots_(new spsc_sema::LightweightSemaphore(0)),
items(new spsc_sema::LightweightSemaphore(0)),
nextSlot(), nextItem()
{
swap(other);
}
BlockingReaderWriterCircularBuffer(BlockingReaderWriterCircularBuffer const&) = delete;
// Note: The queue should not be accessed concurrently while it's
// being deleted. It's up to the user to synchronize this.
~BlockingReaderWriterCircularBuffer()
{
for (std::size_t i = 0, n = items->availableApprox(); i != n; ++i)
reinterpret_cast<T*>(data)[(nextItem + i) & mask].~T();
std::free(rawData);
}
BlockingReaderWriterCircularBuffer& operator=(BlockingReaderWriterCircularBuffer&& other) noexcept
{
swap(other);
return *this;
}
BlockingReaderWriterCircularBuffer& operator=(BlockingReaderWriterCircularBuffer const&) = delete;
// Swaps the contents of this buffer with the contents of another.
// Not thread-safe.
void swap(BlockingReaderWriterCircularBuffer& other) noexcept
{
std::swap(maxcap, other.maxcap);
std::swap(mask, other.mask);
std::swap(rawData, other.rawData);
std::swap(data, other.data);
std::swap(slots_, other.slots_);
std::swap(items, other.items);
std::swap(nextSlot, other.nextSlot);
std::swap(nextItem, other.nextItem);
}
// Enqueues a single item (by copying it).
// Fails if not enough room to enqueue.
// Thread-safe when called by producer thread.
// No exception guarantee (state will be corrupted) if constructor of T throws.
bool try_enqueue(T const& item)
{
if (!slots_->tryWait())
return false;
inner_enqueue(item);
return true;
}
// Enqueues a single item (by moving it, if possible).
// Fails if not enough room to enqueue.
// Thread-safe when called by producer thread.
// No exception guarantee (state will be corrupted) if constructor of T throws.
bool try_enqueue(T&& item)
{
if (!slots_->tryWait())
return false;
inner_enqueue(std::move(item));
return true;
}
// Blocks the current thread until there's enough space to enqueue the given item,
// then enqueues it (via copy).
// Thread-safe when called by producer thread.
// No exception guarantee (state will be corrupted) if constructor of T throws.
void wait_enqueue(T const& item)
{
while (!slots_->wait());
inner_enqueue(item);
}
// Blocks the current thread until there's enough space to enqueue the given item,
// then enqueues it (via move, if possible).
// Thread-safe when called by producer thread.
// No exception guarantee (state will be corrupted) if constructor of T throws.
void wait_enqueue(T&& item)
{
while (!slots_->wait());
inner_enqueue(std::move(item));
}
// Blocks the current thread until there's enough space to enqueue the given item,
// or the timeout expires. Returns false without enqueueing the item if the timeout
// expires, otherwise enqueues the item (via copy) and returns true.
// Thread-safe when called by producer thread.
// No exception guarantee (state will be corrupted) if constructor of T throws.
bool wait_enqueue_timed(T const& item, std::int64_t timeout_usecs)
{
if (!slots_->wait(timeout_usecs))
return false;
inner_enqueue(item);
return true;
}
// Blocks the current thread until there's enough space to enqueue the given item,
// or the timeout expires. Returns false without enqueueing the item if the timeout
// expires, otherwise enqueues the item (via move, if possible) and returns true.
// Thread-safe when called by producer thread.
// No exception guarantee (state will be corrupted) if constructor of T throws.
bool wait_enqueue_timed(T&& item, std::int64_t timeout_usecs)
{
if (!slots_->wait(timeout_usecs))
return false;
inner_enqueue(std::move(item));
return true;
}
// Blocks the current thread until there's enough space to enqueue the given item,
// or the timeout expires. Returns false without enqueueing the item if the timeout
// expires, otherwise enqueues the item (via copy) and returns true.
// Thread-safe when called by producer thread.
// No exception guarantee (state will be corrupted) if constructor of T throws.
template<typename Rep, typename Period>
inline bool wait_enqueue_timed(T const& item, std::chrono::duration<Rep, Period> const& timeout)
{
return wait_enqueue_timed(item, std::chrono::duration_cast<std::chrono::microseconds>(timeout).count());
}
// Blocks the current thread until there's enough space to enqueue the given item,
// or the timeout expires. Returns false without enqueueing the item if the timeout
// expires, otherwise enqueues the item (via move, if possible) and returns true.
// Thread-safe when called by producer thread.
// No exception guarantee (state will be corrupted) if constructor of T throws.
template<typename Rep, typename Period>
inline bool wait_enqueue_timed(T&& item, std::chrono::duration<Rep, Period> const& timeout)
{
return wait_enqueue_timed(std::move(item), std::chrono::duration_cast<std::chrono::microseconds>(timeout).count());
}
// Attempts to dequeue a single item.
// Returns false if the buffer is empty.
// Thread-safe when called by consumer thread.
// No exception guarantee (state will be corrupted) if assignment operator of U throws.
template<typename U>
bool try_dequeue(U& item)
{
if (!items->tryWait())
return false;
inner_dequeue(item);
return true;
}
// Blocks the current thread until there's something to dequeue, then dequeues it.
// Thread-safe when called by consumer thread.
// No exception guarantee (state will be corrupted) if assignment operator of U throws.
template<typename U>
void wait_dequeue(U& item)
{
while (!items->wait());
inner_dequeue(item);
}
// Blocks the current thread until either there's something to dequeue
// or the timeout expires. Returns false without setting `item` if the
// timeout expires, otherwise assigns to `item` and returns true.
// Thread-safe when called by consumer thread.
// No exception guarantee (state will be corrupted) if assignment operator of U throws.
template<typename U>
bool wait_dequeue_timed(U& item, std::int64_t timeout_usecs)
{
if (!items->wait(timeout_usecs))
return false;
inner_dequeue(item);
return true;
}
// Blocks the current thread until either there's something to dequeue
// or the timeout expires. Returns false without setting `item` if the
// timeout expires, otherwise assigns to `item` and returns true.
// Thread-safe when called by consumer thread.
// No exception guarantee (state will be corrupted) if assignment operator of U throws.
template<typename U, typename Rep, typename Period>
inline bool wait_dequeue_timed(U& item, std::chrono::duration<Rep, Period> const& timeout)
{
return wait_dequeue_timed(item, std::chrono::duration_cast<std::chrono::microseconds>(timeout).count());
}
// Returns a pointer to the next element in the queue (the one that would
// be removed next by a call to `try_dequeue` or `try_pop`). If the queue
// appears empty at the time the method is called, returns nullptr instead.
// Thread-safe when called by consumer thread.
inline T* peek()
{
if (!items->availableApprox())
return nullptr;
return inner_peek();
}
// Pops the next element from the queue, if there is one.
// Thread-safe when called by consumer thread.
inline bool try_pop()
{
if (!items->tryWait())
return false;
inner_pop();
return true;
}
// Returns a (possibly outdated) snapshot of the total number of elements currently in the buffer.
// Thread-safe.
inline std::size_t size_approx() const
{
return items->availableApprox();
}
// Returns the maximum number of elements that this circular buffer can hold at once.
// Thread-safe.
inline std::size_t max_capacity() const
{
return maxcap;
}
private:
template<typename U>
void inner_enqueue(U&& item)
{
std::size_t i = nextSlot++;
new (reinterpret_cast<T*>(data) + (i & mask)) T(std::forward<U>(item));
items->signal();
}
template<typename U>
void inner_dequeue(U& item)
{
std::size_t i = nextItem++;
T& element = reinterpret_cast<T*>(data)[i & mask];
item = std::move(element);
element.~T();
slots_->signal();
}
T* inner_peek()
{
return reinterpret_cast<T*>(data) + (nextItem & mask);
}
void inner_pop()
{
std::size_t i = nextItem++;
reinterpret_cast<T*>(data)[i & mask].~T();
slots_->signal();
}
template<typename U>
static inline char* align_for(char* ptr)
{
const std::size_t alignment = std::alignment_of<U>::value;
return ptr + (alignment - (reinterpret_cast<std::uintptr_t>(ptr) % alignment)) % alignment;
}
private:
std::size_t maxcap; // actual (non-power-of-two) capacity
std::size_t mask; // circular buffer capacity mask (for cheap modulo)
char* rawData; // raw circular buffer memory
char* data; // circular buffer memory aligned to element alignment
std::unique_ptr<spsc_sema::LightweightSemaphore> slots_; // number of slots currently free (named with underscore to accommodate Qt's 'slots' macro)
std::unique_ptr<spsc_sema::LightweightSemaphore> items; // number of elements currently enqueued
char cachelineFiller0[MOODYCAMEL_CACHE_LINE_SIZE - sizeof(char*) * 2 - sizeof(std::size_t) * 2 - sizeof(std::unique_ptr<spsc_sema::LightweightSemaphore>) * 2];
std::size_t nextSlot; // index of next free slot to enqueue into
char cachelineFiller1[MOODYCAMEL_CACHE_LINE_SIZE - sizeof(std::size_t)];
std::size_t nextItem; // index of next element to dequeue from
};
}

View file

@ -0,0 +1,979 @@
// ©2013-2020 Cameron Desrochers.
// Distributed under the simplified BSD license (see the license file that
// should have come with this header).
#pragma once
#include "atomicops.h"
#include <new>
#include <type_traits>
#include <utility>
#include <cassert>
#include <stdexcept>
#include <new>
#include <cstdint>
#include <cstdlib> // For malloc/free/abort & size_t
#include <memory>
#if __cplusplus > 199711L || _MSC_VER >= 1700 // C++11 or VS2012
#include <chrono>
#endif
// A lock-free queue for a single-consumer, single-producer architecture.
// The queue is also wait-free in the common path (except if more memory
// needs to be allocated, in which case malloc is called).
// Allocates memory sparingly, and only once if the original maximum size
// estimate is never exceeded.
// Tested on x86/x64 processors, but semantics should be correct for all
// architectures (given the right implementations in atomicops.h), provided
// that aligned integer and pointer accesses are naturally atomic.
// Note that there should only be one consumer thread and producer thread;
// Switching roles of the threads, or using multiple consecutive threads for
// one role, is not safe unless properly synchronized.
// Using the queue exclusively from one thread is fine, though a bit silly.
#ifndef MOODYCAMEL_CACHE_LINE_SIZE
#define MOODYCAMEL_CACHE_LINE_SIZE 64
#endif
#ifndef MOODYCAMEL_EXCEPTIONS_ENABLED
#if (defined(_MSC_VER) && defined(_CPPUNWIND)) || (defined(__GNUC__) && defined(__EXCEPTIONS)) || (!defined(_MSC_VER) && !defined(__GNUC__))
#define MOODYCAMEL_EXCEPTIONS_ENABLED
#endif
#endif
#ifndef MOODYCAMEL_HAS_EMPLACE
#if !defined(_MSC_VER) || _MSC_VER >= 1800 // variadic templates: either a non-MS compiler or VS >= 2013
#define MOODYCAMEL_HAS_EMPLACE 1
#endif
#endif
#ifndef MOODYCAMEL_MAYBE_ALIGN_TO_CACHELINE
#if defined (__APPLE__) && defined (__MACH__) && __cplusplus >= 201703L
// This is required to find out what deployment target we are using
#include <AvailabilityMacros.h>
#if !defined(MAC_OS_X_VERSION_MIN_REQUIRED) || !defined(MAC_OS_X_VERSION_10_14) || MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_14
// C++17 new(size_t, align_val_t) is not backwards-compatible with older versions of macOS, so we can't support over-alignment in this case
#define MOODYCAMEL_MAYBE_ALIGN_TO_CACHELINE
#endif
#endif
#endif
#ifndef MOODYCAMEL_MAYBE_ALIGN_TO_CACHELINE
#define MOODYCAMEL_MAYBE_ALIGN_TO_CACHELINE AE_ALIGN(MOODYCAMEL_CACHE_LINE_SIZE)
#endif
#ifdef AE_VCPP
#pragma warning(push)
#pragma warning(disable: 4324) // structure was padded due to __declspec(align())
#pragma warning(disable: 4820) // padding was added
#pragma warning(disable: 4127) // conditional expression is constant
#endif
namespace moodycamel {
template<typename T, size_t MAX_BLOCK_SIZE = 512>
class MOODYCAMEL_MAYBE_ALIGN_TO_CACHELINE ReaderWriterQueue
{
// Design: Based on a queue-of-queues. The low-level queues are just
// circular buffers with front and tail indices indicating where the
// next element to dequeue is and where the next element can be enqueued,
// respectively. Each low-level queue is called a "block". Each block
// wastes exactly one element's worth of space to keep the design simple
// (if front == tail then the queue is empty, and can't be full).
// The high-level queue is a circular linked list of blocks; again there
// is a front and tail, but this time they are pointers to the blocks.
// The front block is where the next element to be dequeued is, provided
// the block is not empty. The back block is where elements are to be
// enqueued, provided the block is not full.
// The producer thread owns all the tail indices/pointers. The consumer
// thread owns all the front indices/pointers. Both threads read each
// other's variables, but only the owning thread updates them. E.g. After
// the consumer reads the producer's tail, the tail may change before the
// consumer is done dequeuing an object, but the consumer knows the tail
// will never go backwards, only forwards.
// If there is no room to enqueue an object, an additional block (of
// equal size to the last block) is added. Blocks are never removed.
public:
typedef T value_type;
// Constructs a queue that can hold at least `size` elements without further
// allocations. If more than MAX_BLOCK_SIZE elements are requested,
// then several blocks of MAX_BLOCK_SIZE each are reserved (including
// at least one extra buffer block).
AE_NO_TSAN explicit ReaderWriterQueue(size_t size = 15)
#ifndef NDEBUG
: enqueuing(false)
,dequeuing(false)
#endif
{
assert(MAX_BLOCK_SIZE == ceilToPow2(MAX_BLOCK_SIZE) && "MAX_BLOCK_SIZE must be a power of 2");
assert(MAX_BLOCK_SIZE >= 2 && "MAX_BLOCK_SIZE must be at least 2");
Block* firstBlock = nullptr;
largestBlockSize = ceilToPow2(size + 1); // We need a spare slot to fit size elements in the block
if (largestBlockSize > MAX_BLOCK_SIZE * 2) {
// We need a spare block in case the producer is writing to a different block the consumer is reading from, and
// wants to enqueue the maximum number of elements. We also need a spare element in each block to avoid the ambiguity
// between front == tail meaning "empty" and "full".
// So the effective number of slots that are guaranteed to be usable at any time is the block size - 1 times the
// number of blocks - 1. Solving for size and applying a ceiling to the division gives us (after simplifying):
size_t initialBlockCount = (size + MAX_BLOCK_SIZE * 2 - 3) / (MAX_BLOCK_SIZE - 1);
largestBlockSize = MAX_BLOCK_SIZE;
Block* lastBlock = nullptr;
for (size_t i = 0; i != initialBlockCount; ++i) {
auto block = make_block(largestBlockSize);
if (block == nullptr) {
#ifdef MOODYCAMEL_EXCEPTIONS_ENABLED
throw std::bad_alloc();
#else
abort();
#endif
}
if (firstBlock == nullptr) {
firstBlock = block;
}
else {
lastBlock->next = block;
}
lastBlock = block;
block->next = firstBlock;
}
}
else {
firstBlock = make_block(largestBlockSize);
if (firstBlock == nullptr) {
#ifdef MOODYCAMEL_EXCEPTIONS_ENABLED
throw std::bad_alloc();
#else
abort();
#endif
}
firstBlock->next = firstBlock;
}
frontBlock = firstBlock;
tailBlock = firstBlock;
// Make sure the reader/writer threads will have the initialized memory setup above:
fence(memory_order_sync);
}
// Note: The queue should not be accessed concurrently while it's
// being moved. It's up to the user to synchronize this.
AE_NO_TSAN ReaderWriterQueue(ReaderWriterQueue&& other)
: frontBlock(other.frontBlock.load()),
tailBlock(other.tailBlock.load()),
largestBlockSize(other.largestBlockSize)
#ifndef NDEBUG
,enqueuing(false)
,dequeuing(false)
#endif
{
other.largestBlockSize = 32;
Block* b = other.make_block(other.largestBlockSize);
if (b == nullptr) {
#ifdef MOODYCAMEL_EXCEPTIONS_ENABLED
throw std::bad_alloc();
#else
abort();
#endif
}
b->next = b;
other.frontBlock = b;
other.tailBlock = b;
}
// Note: The queue should not be accessed concurrently while it's
// being moved. It's up to the user to synchronize this.
ReaderWriterQueue& operator=(ReaderWriterQueue&& other) AE_NO_TSAN
{
Block* b = frontBlock.load();
frontBlock = other.frontBlock.load();
other.frontBlock = b;
b = tailBlock.load();
tailBlock = other.tailBlock.load();
other.tailBlock = b;
std::swap(largestBlockSize, other.largestBlockSize);
return *this;
}
// Note: The queue should not be accessed concurrently while it's
// being deleted. It's up to the user to synchronize this.
AE_NO_TSAN ~ReaderWriterQueue()
{
// Make sure we get the latest version of all variables from other CPUs:
fence(memory_order_sync);
// Destroy any remaining objects in queue and free memory
Block* frontBlock_ = frontBlock;
Block* block = frontBlock_;
do {
Block* nextBlock = block->next;
size_t blockFront = block->front;
size_t blockTail = block->tail;
for (size_t i = blockFront; i != blockTail; i = (i + 1) & block->sizeMask) {
auto element = reinterpret_cast<T*>(block->data + i * sizeof(T));
element->~T();
(void)element;
}
auto rawBlock = block->rawThis;
block->~Block();
std::free(rawBlock);
block = nextBlock;
} while (block != frontBlock_);
}
// Enqueues a copy of element if there is room in the queue.
// Returns true if the element was enqueued, false otherwise.
// Does not allocate memory.
AE_FORCEINLINE bool try_enqueue(T const& element) AE_NO_TSAN
{
return inner_enqueue<CannotAlloc>(element);
}
// Enqueues a moved copy of element if there is room in the queue.
// Returns true if the element was enqueued, false otherwise.
// Does not allocate memory.
AE_FORCEINLINE bool try_enqueue(T&& element) AE_NO_TSAN
{
return inner_enqueue<CannotAlloc>(std::forward<T>(element));
}
#if MOODYCAMEL_HAS_EMPLACE
// Like try_enqueue() but with emplace semantics (i.e. construct-in-place).
template<typename... Args>
AE_FORCEINLINE bool try_emplace(Args&&... args) AE_NO_TSAN
{
return inner_enqueue<CannotAlloc>(std::forward<Args>(args)...);
}
#endif
// Enqueues a copy of element on the queue.
// Allocates an additional block of memory if needed.
// Only fails (returns false) if memory allocation fails.
AE_FORCEINLINE bool enqueue(T const& element) AE_NO_TSAN
{
return inner_enqueue<CanAlloc>(element);
}
// Enqueues a moved copy of element on the queue.
// Allocates an additional block of memory if needed.
// Only fails (returns false) if memory allocation fails.
AE_FORCEINLINE bool enqueue(T&& element) AE_NO_TSAN
{
return inner_enqueue<CanAlloc>(std::forward<T>(element));
}
#if MOODYCAMEL_HAS_EMPLACE
// Like enqueue() but with emplace semantics (i.e. construct-in-place).
template<typename... Args>
AE_FORCEINLINE bool emplace(Args&&... args) AE_NO_TSAN
{
return inner_enqueue<CanAlloc>(std::forward<Args>(args)...);
}
#endif
// Attempts to dequeue an element; if the queue is empty,
// returns false instead. If the queue has at least one element,
// moves front to result using operator=, then returns true.
template<typename U>
bool try_dequeue(U& result) AE_NO_TSAN
{
#ifndef NDEBUG
ReentrantGuard guard(this->dequeuing);
#endif
// High-level pseudocode:
// Remember where the tail block is
// If the front block has an element in it, dequeue it
// Else
// If front block was the tail block when we entered the function, return false
// Else advance to next block and dequeue the item there
// Note that we have to use the value of the tail block from before we check if the front
// block is full or not, in case the front block is empty and then, before we check if the
// tail block is at the front block or not, the producer fills up the front block *and
// moves on*, which would make us skip a filled block. Seems unlikely, but was consistently
// reproducible in practice.
// In order to avoid overhead in the common case, though, we do a double-checked pattern
// where we have the fast path if the front block is not empty, then read the tail block,
// then re-read the front block and check if it's not empty again, then check if the tail
// block has advanced.
Block* frontBlock_ = frontBlock.load();
size_t blockTail = frontBlock_->localTail;
size_t blockFront = frontBlock_->front.load();
if (blockFront != blockTail || blockFront != (frontBlock_->localTail = frontBlock_->tail.load())) {
fence(memory_order_acquire);
non_empty_front_block:
// Front block not empty, dequeue from here
auto element = reinterpret_cast<T*>(frontBlock_->data + blockFront * sizeof(T));
result = std::move(*element);
element->~T();
blockFront = (blockFront + 1) & frontBlock_->sizeMask;
fence(memory_order_release);
frontBlock_->front = blockFront;
}
else if (frontBlock_ != tailBlock.load()) {
fence(memory_order_acquire);
frontBlock_ = frontBlock.load();
blockTail = frontBlock_->localTail = frontBlock_->tail.load();
blockFront = frontBlock_->front.load();
fence(memory_order_acquire);
if (blockFront != blockTail) {
// Oh look, the front block isn't empty after all
goto non_empty_front_block;
}
// Front block is empty but there's another block ahead, advance to it
Block* nextBlock = frontBlock_->next;
// Don't need an acquire fence here since next can only ever be set on the tailBlock,
// and we're not the tailBlock, and we did an acquire earlier after reading tailBlock which
// ensures next is up-to-date on this CPU in case we recently were at tailBlock.
size_t nextBlockFront = nextBlock->front.load();
size_t nextBlockTail = nextBlock->localTail = nextBlock->tail.load();
fence(memory_order_acquire);
// Since the tailBlock is only ever advanced after being written to,
// we know there's for sure an element to dequeue on it
assert(nextBlockFront != nextBlockTail);
AE_UNUSED(nextBlockTail);
// We're done with this block, let the producer use it if it needs
fence(memory_order_release); // Expose possibly pending changes to frontBlock->front from last dequeue
frontBlock = frontBlock_ = nextBlock;
compiler_fence(memory_order_release); // Not strictly needed
auto element = reinterpret_cast<T*>(frontBlock_->data + nextBlockFront * sizeof(T));
result = std::move(*element);
element->~T();
nextBlockFront = (nextBlockFront + 1) & frontBlock_->sizeMask;
fence(memory_order_release);
frontBlock_->front = nextBlockFront;
}
else {
// No elements in current block and no other block to advance to
return false;
}
return true;
}
// Returns a pointer to the front element in the queue (the one that
// would be removed next by a call to `try_dequeue` or `pop`). If the
// queue appears empty at the time the method is called, nullptr is
// returned instead.
// Must be called only from the consumer thread.
T* peek() const AE_NO_TSAN
{
#ifndef NDEBUG
ReentrantGuard guard(this->dequeuing);
#endif
// See try_dequeue() for reasoning
Block* frontBlock_ = frontBlock.load();
size_t blockTail = frontBlock_->localTail;
size_t blockFront = frontBlock_->front.load();
if (blockFront != blockTail || blockFront != (frontBlock_->localTail = frontBlock_->tail.load())) {
fence(memory_order_acquire);
non_empty_front_block:
return reinterpret_cast<T*>(frontBlock_->data + blockFront * sizeof(T));
}
else if (frontBlock_ != tailBlock.load()) {
fence(memory_order_acquire);
frontBlock_ = frontBlock.load();
blockTail = frontBlock_->localTail = frontBlock_->tail.load();
blockFront = frontBlock_->front.load();
fence(memory_order_acquire);
if (blockFront != blockTail) {
goto non_empty_front_block;
}
Block* nextBlock = frontBlock_->next;
size_t nextBlockFront = nextBlock->front.load();
fence(memory_order_acquire);
assert(nextBlockFront != nextBlock->tail.load());
return reinterpret_cast<T*>(nextBlock->data + nextBlockFront * sizeof(T));
}
return nullptr;
}
// Removes the front element from the queue, if any, without returning it.
// Returns true on success, or false if the queue appeared empty at the time
// `pop` was called.
bool pop() AE_NO_TSAN
{
#ifndef NDEBUG
ReentrantGuard guard(this->dequeuing);
#endif
// See try_dequeue() for reasoning
Block* frontBlock_ = frontBlock.load();
size_t blockTail = frontBlock_->localTail;
size_t blockFront = frontBlock_->front.load();
if (blockFront != blockTail || blockFront != (frontBlock_->localTail = frontBlock_->tail.load())) {
fence(memory_order_acquire);
non_empty_front_block:
auto element = reinterpret_cast<T*>(frontBlock_->data + blockFront * sizeof(T));
element->~T();
blockFront = (blockFront + 1) & frontBlock_->sizeMask;
fence(memory_order_release);
frontBlock_->front = blockFront;
}
else if (frontBlock_ != tailBlock.load()) {
fence(memory_order_acquire);
frontBlock_ = frontBlock.load();
blockTail = frontBlock_->localTail = frontBlock_->tail.load();
blockFront = frontBlock_->front.load();
fence(memory_order_acquire);
if (blockFront != blockTail) {
goto non_empty_front_block;
}
// Front block is empty but there's another block ahead, advance to it
Block* nextBlock = frontBlock_->next;
size_t nextBlockFront = nextBlock->front.load();
size_t nextBlockTail = nextBlock->localTail = nextBlock->tail.load();
fence(memory_order_acquire);
assert(nextBlockFront != nextBlockTail);
AE_UNUSED(nextBlockTail);
fence(memory_order_release);
frontBlock = frontBlock_ = nextBlock;
compiler_fence(memory_order_release);
auto element = reinterpret_cast<T*>(frontBlock_->data + nextBlockFront * sizeof(T));
element->~T();
nextBlockFront = (nextBlockFront + 1) & frontBlock_->sizeMask;
fence(memory_order_release);
frontBlock_->front = nextBlockFront;
}
else {
// No elements in current block and no other block to advance to
return false;
}
return true;
}
// Returns the approximate number of items currently in the queue.
// Safe to call from both the producer and consumer threads.
inline size_t size_approx() const AE_NO_TSAN
{
size_t result = 0;
Block* frontBlock_ = frontBlock.load();
Block* block = frontBlock_;
do {
fence(memory_order_acquire);
size_t blockFront = block->front.load();
size_t blockTail = block->tail.load();
result += (blockTail - blockFront) & block->sizeMask;
block = block->next.load();
} while (block != frontBlock_);
return result;
}
// Returns the total number of items that could be enqueued without incurring
// an allocation when this queue is empty.
// Safe to call from both the producer and consumer threads.
//
// NOTE: The actual capacity during usage may be different depending on the consumer.
// If the consumer is removing elements concurrently, the producer cannot add to
// the block the consumer is removing from until it's completely empty, except in
// the case where the producer was writing to the same block the consumer was
// reading from the whole time.
inline size_t max_capacity() const {
size_t result = 0;
Block* frontBlock_ = frontBlock.load();
Block* block = frontBlock_;
do {
fence(memory_order_acquire);
result += block->sizeMask;
block = block->next.load();
} while (block != frontBlock_);
return result;
}
private:
enum AllocationMode { CanAlloc, CannotAlloc };
#if MOODYCAMEL_HAS_EMPLACE
template<AllocationMode canAlloc, typename... Args>
bool inner_enqueue(Args&&... args) AE_NO_TSAN
#else
template<AllocationMode canAlloc, typename U>
bool inner_enqueue(U&& element) AE_NO_TSAN
#endif
{
#ifndef NDEBUG
ReentrantGuard guard(this->enqueuing);
#endif
// High-level pseudocode (assuming we're allowed to alloc a new block):
// If room in tail block, add to tail
// Else check next block
// If next block is not the head block, enqueue on next block
// Else create a new block and enqueue there
// Advance tail to the block we just enqueued to
Block* tailBlock_ = tailBlock.load();
size_t blockFront = tailBlock_->localFront;
size_t blockTail = tailBlock_->tail.load();
size_t nextBlockTail = (blockTail + 1) & tailBlock_->sizeMask;
if (nextBlockTail != blockFront || nextBlockTail != (tailBlock_->localFront = tailBlock_->front.load())) {
fence(memory_order_acquire);
// This block has room for at least one more element
char* location = tailBlock_->data + blockTail * sizeof(T);
#if MOODYCAMEL_HAS_EMPLACE
new (location) T(std::forward<Args>(args)...);
#else
new (location) T(std::forward<U>(element));
#endif
fence(memory_order_release);
tailBlock_->tail = nextBlockTail;
}
else {
fence(memory_order_acquire);
if (tailBlock_->next.load() != frontBlock) {
// Note that the reason we can't advance to the frontBlock and start adding new entries there
// is because if we did, then dequeue would stay in that block, eventually reading the new values,
// instead of advancing to the next full block (whose values were enqueued first and so should be
// consumed first).
fence(memory_order_acquire); // Ensure we get latest writes if we got the latest frontBlock
// tailBlock is full, but there's a free block ahead, use it
Block* tailBlockNext = tailBlock_->next.load();
size_t nextBlockFront = tailBlockNext->localFront = tailBlockNext->front.load();
nextBlockTail = tailBlockNext->tail.load();
fence(memory_order_acquire);
// This block must be empty since it's not the head block and we
// go through the blocks in a circle
assert(nextBlockFront == nextBlockTail);
tailBlockNext->localFront = nextBlockFront;
char* location = tailBlockNext->data + nextBlockTail * sizeof(T);
#if MOODYCAMEL_HAS_EMPLACE
new (location) T(std::forward<Args>(args)...);
#else
new (location) T(std::forward<U>(element));
#endif
tailBlockNext->tail = (nextBlockTail + 1) & tailBlockNext->sizeMask;
fence(memory_order_release);
tailBlock = tailBlockNext;
}
else if (canAlloc == CanAlloc) {
// tailBlock is full and there's no free block ahead; create a new block
auto newBlockSize = largestBlockSize >= MAX_BLOCK_SIZE ? largestBlockSize : largestBlockSize * 2;
auto newBlock = make_block(newBlockSize);
if (newBlock == nullptr) {
// Could not allocate a block!
return false;
}
largestBlockSize = newBlockSize;
#if MOODYCAMEL_HAS_EMPLACE
new (newBlock->data) T(std::forward<Args>(args)...);
#else
new (newBlock->data) T(std::forward<U>(element));
#endif
assert(newBlock->front == 0);
newBlock->tail = newBlock->localTail = 1;
newBlock->next = tailBlock_->next.load();
tailBlock_->next = newBlock;
// Might be possible for the dequeue thread to see the new tailBlock->next
// *without* seeing the new tailBlock value, but this is OK since it can't
// advance to the next block until tailBlock is set anyway (because the only
// case where it could try to read the next is if it's already at the tailBlock,
// and it won't advance past tailBlock in any circumstance).
fence(memory_order_release);
tailBlock = newBlock;
}
else if (canAlloc == CannotAlloc) {
// Would have had to allocate a new block to enqueue, but not allowed
return false;
}
else {
assert(false && "Should be unreachable code");
return false;
}
}
return true;
}
// Disable copying
ReaderWriterQueue(ReaderWriterQueue const&) { }
// Disable assignment
ReaderWriterQueue& operator=(ReaderWriterQueue const&) { }
AE_FORCEINLINE static size_t ceilToPow2(size_t x)
{
// From http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
--x;
x |= x >> 1;
x |= x >> 2;
x |= x >> 4;
for (size_t i = 1; i < sizeof(size_t); i <<= 1) {
x |= x >> (i << 3);
}
++x;
return x;
}
template<typename U>
static AE_FORCEINLINE char* align_for(char* ptr) AE_NO_TSAN
{
const std::size_t alignment = std::alignment_of<U>::value;
return ptr + (alignment - (reinterpret_cast<std::uintptr_t>(ptr) % alignment)) % alignment;
}
private:
#ifndef NDEBUG
struct ReentrantGuard
{
AE_NO_TSAN ReentrantGuard(weak_atomic<bool>& _inSection)
: inSection(_inSection)
{
assert(!inSection && "Concurrent (or re-entrant) enqueue or dequeue operation detected (only one thread at a time may hold the producer or consumer role)");
inSection = true;
}
AE_NO_TSAN ~ReentrantGuard() { inSection = false; }
private:
ReentrantGuard& operator=(ReentrantGuard const&);
private:
weak_atomic<bool>& inSection;
};
#endif
struct Block
{
// Avoid false-sharing by putting highly contended variables on their own cache lines
weak_atomic<size_t> front; // (Atomic) Elements are read from here
size_t localTail; // An uncontended shadow copy of tail, owned by the consumer
char cachelineFiller0[MOODYCAMEL_CACHE_LINE_SIZE - sizeof(weak_atomic<size_t>) - sizeof(size_t)];
weak_atomic<size_t> tail; // (Atomic) Elements are enqueued here
size_t localFront;
char cachelineFiller1[MOODYCAMEL_CACHE_LINE_SIZE - sizeof(weak_atomic<size_t>) - sizeof(size_t)]; // next isn't very contended, but we don't want it on the same cache line as tail (which is)
weak_atomic<Block*> next; // (Atomic)
char* data; // Contents (on heap) are aligned to T's alignment
const size_t sizeMask;
// size must be a power of two (and greater than 0)
AE_NO_TSAN Block(size_t const& _size, char* _rawThis, char* _data)
: front(0UL), localTail(0), tail(0UL), localFront(0), next(nullptr), data(_data), sizeMask(_size - 1), rawThis(_rawThis)
{
}
private:
// C4512 - Assignment operator could not be generated
Block& operator=(Block const&);
public:
char* rawThis;
};
static Block* make_block(size_t capacity) AE_NO_TSAN
{
// Allocate enough memory for the block itself, as well as all the elements it will contain
auto size = sizeof(Block) + std::alignment_of<Block>::value - 1;
size += sizeof(T) * capacity + std::alignment_of<T>::value - 1;
auto newBlockRaw = static_cast<char*>(std::malloc(size));
if (newBlockRaw == nullptr) {
return nullptr;
}
auto newBlockAligned = align_for<Block>(newBlockRaw);
auto newBlockData = align_for<T>(newBlockAligned + sizeof(Block));
return new (newBlockAligned) Block(capacity, newBlockRaw, newBlockData);
}
private:
weak_atomic<Block*> frontBlock; // (Atomic) Elements are dequeued from this block
char cachelineFiller[MOODYCAMEL_CACHE_LINE_SIZE - sizeof(weak_atomic<Block*>)];
weak_atomic<Block*> tailBlock; // (Atomic) Elements are enqueued to this block
size_t largestBlockSize;
#ifndef NDEBUG
weak_atomic<bool> enqueuing;
mutable weak_atomic<bool> dequeuing;
#endif
};
// Like ReaderWriterQueue, but also providees blocking operations
template<typename T, size_t MAX_BLOCK_SIZE = 512>
class BlockingReaderWriterQueue
{
private:
typedef ::moodycamel::ReaderWriterQueue<T, MAX_BLOCK_SIZE> ReaderWriterQueue;
public:
explicit BlockingReaderWriterQueue(size_t size = 15) AE_NO_TSAN
: inner(size), sema(new spsc_sema::LightweightSemaphore())
{ }
BlockingReaderWriterQueue(BlockingReaderWriterQueue&& other) AE_NO_TSAN
: inner(std::move(other.inner)), sema(std::move(other.sema))
{ }
BlockingReaderWriterQueue& operator=(BlockingReaderWriterQueue&& other) AE_NO_TSAN
{
std::swap(sema, other.sema);
std::swap(inner, other.inner);
return *this;
}
// Enqueues a copy of element if there is room in the queue.
// Returns true if the element was enqueued, false otherwise.
// Does not allocate memory.
AE_FORCEINLINE bool try_enqueue(T const& element) AE_NO_TSAN
{
if (inner.try_enqueue(element)) {
sema->signal();
return true;
}
return false;
}
// Enqueues a moved copy of element if there is room in the queue.
// Returns true if the element was enqueued, false otherwise.
// Does not allocate memory.
AE_FORCEINLINE bool try_enqueue(T&& element) AE_NO_TSAN
{
if (inner.try_enqueue(std::forward<T>(element))) {
sema->signal();
return true;
}
return false;
}
#if MOODYCAMEL_HAS_EMPLACE
// Like try_enqueue() but with emplace semantics (i.e. construct-in-place).
template<typename... Args>
AE_FORCEINLINE bool try_emplace(Args&&... args) AE_NO_TSAN
{
if (inner.try_emplace(std::forward<Args>(args)...)) {
sema->signal();
return true;
}
return false;
}
#endif
// Enqueues a copy of element on the queue.
// Allocates an additional block of memory if needed.
// Only fails (returns false) if memory allocation fails.
AE_FORCEINLINE bool enqueue(T const& element) AE_NO_TSAN
{
if (inner.enqueue(element)) {
sema->signal();
return true;
}
return false;
}
// Enqueues a moved copy of element on the queue.
// Allocates an additional block of memory if needed.
// Only fails (returns false) if memory allocation fails.
AE_FORCEINLINE bool enqueue(T&& element) AE_NO_TSAN
{
if (inner.enqueue(std::forward<T>(element))) {
sema->signal();
return true;
}
return false;
}
#if MOODYCAMEL_HAS_EMPLACE
// Like enqueue() but with emplace semantics (i.e. construct-in-place).
template<typename... Args>
AE_FORCEINLINE bool emplace(Args&&... args) AE_NO_TSAN
{
if (inner.emplace(std::forward<Args>(args)...)) {
sema->signal();
return true;
}
return false;
}
#endif
// Attempts to dequeue an element; if the queue is empty,
// returns false instead. If the queue has at least one element,
// moves front to result using operator=, then returns true.
template<typename U>
bool try_dequeue(U& result) AE_NO_TSAN
{
if (sema->tryWait()) {
bool success = inner.try_dequeue(result);
assert(success);
AE_UNUSED(success);
return true;
}
return false;
}
// Attempts to dequeue an element; if the queue is empty,
// waits until an element is available, then dequeues it.
template<typename U>
void wait_dequeue(U& result) AE_NO_TSAN
{
while (!sema->wait());
bool success = inner.try_dequeue(result);
AE_UNUSED(result);
assert(success);
AE_UNUSED(success);
}
// Attempts to dequeue an element; if the queue is empty,
// waits until an element is available up to the specified timeout,
// then dequeues it and returns true, or returns false if the timeout
// expires before an element can be dequeued.
// Using a negative timeout indicates an indefinite timeout,
// and is thus functionally equivalent to calling wait_dequeue.
template<typename U>
bool wait_dequeue_timed(U& result, std::int64_t timeout_usecs) AE_NO_TSAN
{
if (!sema->wait(timeout_usecs)) {
return false;
}
bool success = inner.try_dequeue(result);
AE_UNUSED(result);
assert(success);
AE_UNUSED(success);
return true;
}
#if __cplusplus > 199711L || _MSC_VER >= 1700
// Attempts to dequeue an element; if the queue is empty,
// waits until an element is available up to the specified timeout,
// then dequeues it and returns true, or returns false if the timeout
// expires before an element can be dequeued.
// Using a negative timeout indicates an indefinite timeout,
// and is thus functionally equivalent to calling wait_dequeue.
template<typename U, typename Rep, typename Period>
inline bool wait_dequeue_timed(U& result, std::chrono::duration<Rep, Period> const& timeout) AE_NO_TSAN
{
return wait_dequeue_timed(result, std::chrono::duration_cast<std::chrono::microseconds>(timeout).count());
}
#endif
// Returns a pointer to the front element in the queue (the one that
// would be removed next by a call to `try_dequeue` or `pop`). If the
// queue appears empty at the time the method is called, nullptr is
// returned instead.
// Must be called only from the consumer thread.
AE_FORCEINLINE T* peek() const AE_NO_TSAN
{
return inner.peek();
}
// Removes the front element from the queue, if any, without returning it.
// Returns true on success, or false if the queue appeared empty at the time
// `pop` was called.
AE_FORCEINLINE bool pop() AE_NO_TSAN
{
if (sema->tryWait()) {
bool result = inner.pop();
assert(result);
AE_UNUSED(result);
return true;
}
return false;
}
// Returns the approximate number of items currently in the queue.
// Safe to call from both the producer and consumer threads.
AE_FORCEINLINE size_t size_approx() const AE_NO_TSAN
{
return sema->availableApprox();
}
// Returns the total number of items that could be enqueued without incurring
// an allocation when this queue is empty.
// Safe to call from both the producer and consumer threads.
//
// NOTE: The actual capacity during usage may be different depending on the consumer.
// If the consumer is removing elements concurrently, the producer cannot add to
// the block the consumer is removing from until it's completely empty, except in
// the case where the producer was writing to the same block the consumer was
// reading from the whole time.
AE_FORCEINLINE size_t max_capacity() const {
return inner.max_capacity();
}
private:
// Disable copying & assignment
BlockingReaderWriterQueue(BlockingReaderWriterQueue const&) { }
BlockingReaderWriterQueue& operator=(BlockingReaderWriterQueue const&) { }
private:
ReaderWriterQueue inner;
std::unique_ptr<spsc_sema::LightweightSemaphore> sema;
};
} // end namespace moodycamel
#ifdef AE_VCPP
#pragma warning(pop)
#endif

View file

@ -0,0 +1,3 @@
@PACKAGE_INIT@
include(${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake)

View file

@ -0,0 +1,82 @@
#include "simplethread.h"
#if defined(_WIN32)
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
struct SimpleThread::ThreadRef
{
HANDLE handle;
static DWORD WINAPI ThreadProc(LPVOID param)
{
auto threadRef = static_cast<ThreadRef*>(param);
threadRef->callbackFunc(threadRef->callbackObj);
return 0;
}
ThreadRef(void* callbackObj, CallbackFunc callbackFunc)
: callbackObj(callbackObj), callbackFunc(callbackFunc)
{
}
void* callbackObj;
CallbackFunc callbackFunc;
};
void SimpleThread::startThread(void* callbackObj, CallbackFunc callbackFunc)
{
thread = new ThreadRef(callbackObj, callbackFunc);
thread->handle = CreateThread(NULL, StackSize, &ThreadRef::ThreadProc, thread, 0, NULL);
}
void SimpleThread::join()
{
if (thread != nullptr && thread->handle != NULL) {
WaitForSingleObject(thread->handle, INFINITE);
CloseHandle(thread->handle);
thread->handle = NULL;
}
}
#else
#include <thread>
struct SimpleThread::ThreadRef
{
std::thread thread;
static void threadProc(ThreadRef* threadRef)
{
threadRef->callbackFunc(threadRef->callbackObj);
}
ThreadRef(void* callbackObj, CallbackFunc callbackFunc)
: callbackObj(callbackObj), callbackFunc(callbackFunc)
{
}
void* callbackObj;
CallbackFunc callbackFunc;
};
void SimpleThread::startThread(void* callbackObj, CallbackFunc callbackFunc)
{
thread = new ThreadRef(callbackObj, callbackFunc);
thread->thread = std::thread(&ThreadRef::threadProc, thread);
}
void SimpleThread::join()
{
if (thread != nullptr && thread->thread.joinable()) {
thread->thread.join();
}
}
#endif
SimpleThread::~SimpleThread()
{
if (thread != nullptr) {
join();
delete thread;
}
}

View file

@ -0,0 +1,154 @@
#pragma once
// Like C++11's std::thread, but with a reduced API, and works on Windows with MSVC2010+.
// Wraps std::thread on other OSes. Perhaps the most significant departure between
// std::thread and this mini-library is that join() is called implicitly in the destructor,
// if the thread is joinable. The thread callback functions should not throw exceptions.
#include <utility>
#include <type_traits>
namespace details
{
template<typename TArg1 = void, typename TArg2 = void, typename TArg3 = void>
struct ArgWrapper
{
typename std::remove_reference<TArg1>::type arg1;
typename std::remove_reference<TArg2>::type arg2;
typename std::remove_reference<TArg3>::type arg3;
template<typename T, typename U, typename V>
ArgWrapper(T&& a1, U&& a2, V&& a3) : arg1(std::forward<T>(a1)), arg2(std::forward<U>(a2)), arg3(std::forward<V>(a3)) { }
template<typename TCallback>
void callCallback(TCallback&& callback) const { std::forward<TCallback>(callback)(std::move(arg1), std::move(arg2), std::move(arg3)); }
};
template<typename TArg1, typename TArg2>
struct ArgWrapper<TArg1, TArg2, void>
{
typename std::remove_reference<TArg1>::type arg1;
typename std::remove_reference<TArg2>::type arg2;
template<typename T, typename U>
ArgWrapper(T&& a1, U&& a2) : arg1(std::forward<T>(a1)), arg2(std::forward<U>(a2)) { }
template<typename TCallback>
void callCallback(TCallback&& callback) const { std::forward<TCallback>(callback)(std::move(arg1), std::move(arg2)); }
};
template<typename TArg1>
struct ArgWrapper<TArg1, void, void>
{
typename std::remove_reference<TArg1>::type arg1;
template<typename T>
ArgWrapper(T&& a1) : arg1(std::forward<T>(a1)) { }
template<typename TCallback>
void callCallback(TCallback&& callback) const { std::forward<TCallback>(callback)(std::move(arg1)); }
};
template<> struct ArgWrapper<void, void, void>
{
template<typename TCallback> void callCallback(TCallback&& callback) const { std::forward<TCallback>(callback)(); }
};
}
class SimpleThread
{
private:
struct ThreadRef;
template<typename TCallback, typename TArgs>
struct CallbackWrapper
{
template<typename U>
CallbackWrapper(TCallback&& callback, U&& args)
: callback(std::forward<TCallback>(callback)), args(std::forward<U>(args))
{
}
static void callAndDelete(void* wrapper)
{
auto typedWrapper = static_cast<CallbackWrapper*>(wrapper);
typedWrapper->args.callCallback(std::move(typedWrapper->callback));
delete typedWrapper;
}
typename std::decay<TCallback>::type callback;
TArgs args;
};
typedef void (*CallbackFunc)(void*);
void startThread(void* callbackObj, CallbackFunc callbackFunc);
public:
static const int StackSize = 4 * 1024; // bytes
SimpleThread() : thread(nullptr) { }
SimpleThread(SimpleThread&& other)
: thread(other.thread)
{
other.thread = nullptr;
}
SimpleThread& operator=(SimpleThread&& other)
{
thread = other.thread;
other.thread = nullptr;
return *this;
}
// Disable copying and copy-assignment
private:
SimpleThread(SimpleThread const&);
SimpleThread& operator=(SimpleThread const&);
public:
template<typename TCallback>
explicit SimpleThread(TCallback&& callback)
{
auto wrapper = new CallbackWrapper<TCallback, details::ArgWrapper<>>(
std::forward<TCallback>(callback),
details::ArgWrapper<>()
);
startThread(wrapper, &CallbackWrapper<TCallback, details::ArgWrapper<>>::callAndDelete);
}
template<typename TCallback, typename TArg1>
explicit SimpleThread(TCallback&& callback, TArg1&& arg1)
{
auto wrapper = new CallbackWrapper<TCallback, details::ArgWrapper<TArg1>>(
std::forward<TCallback>(callback),
details::ArgWrapper<TArg1>(std::forward<TArg1>(arg1))
);
startThread(wrapper, &CallbackWrapper<TCallback, details::ArgWrapper<TArg1>>::callAndDelete);
}
template<typename TCallback, typename TArg1, typename TArg2>
explicit SimpleThread(TCallback&& callback, TArg1&& arg1, TArg2&& arg2)
{
auto wrapper = new CallbackWrapper<TCallback, details::ArgWrapper<TArg1, TArg2>>(
std::forward<TCallback>(callback),
details::ArgWrapper<TArg1, TArg2>(std::forward<TArg1>(arg1), std::forward<TArg2>(arg2))
);
startThread(wrapper, &CallbackWrapper<TCallback, details::ArgWrapper<TArg1, TArg2>>::callAndDelete);
}
template<typename TCallback, typename TArg1, typename TArg2, typename TArg3>
explicit SimpleThread(TCallback&& callback, TArg1&& arg1, TArg2&& arg2, TArg3&& arg3)
{
auto wrapper = new CallbackWrapper<TCallback, details::ArgWrapper<TArg1, TArg2, TArg3>>(
std::forward<TCallback>(callback),
details::ArgWrapper<TArg1, TArg2, TArg3>(std::forward<TArg1>(arg1), std::forward<TArg2>(arg2), std::forward<TArg3>(arg3))
);
startThread(wrapper, &CallbackWrapper<TCallback, details::ArgWrapper<TArg1, TArg2, TArg3>>::callAndDelete);
}
~SimpleThread();
void join();
private:
ThreadRef* thread;
};

View file

@ -0,0 +1,24 @@
ifeq ($(OS),Windows_NT)
EXT=.exe
PLATFORM_OPTS=-static
PLATFORM_LD_OPTS=-Wl,--no-as-needed
else
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S),Darwin)
EXT=
PLATFORM_OPTS=
PLATFORM_LD_OPTS=
else
EXT=
PLATFORM_OPTS=
PLATFORM_LD_OPTS=-lrt -Wl,--no-as-needed
endif
endif
default: stabtest$(EXT)
stabtest$(EXT): stabtest.cpp ../../readerwriterqueue.h ../../atomicops.h ../common/simplethread.h ../common/simplethread.cpp makefile
g++ $(PLATFORM_OPTS) -std=c++11 -Wsign-conversion -Wpedantic -Wall -DNDEBUG -O3 stabtest.cpp ../common/simplethread.cpp -o stabtest$(EXT) -pthread $(PLATFORM_LD_OPTS)
run: stabtest$(EXT)
./stabtest$(EXT)

View file

@ -0,0 +1,26 @@

Microsoft Visual Studio Solution File, Format Version 11.00
# Visual Studio 2010
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "stabtest", "stabtest.vcxproj", "{16E74A53-972D-4762-BC18-8946FB1EF452}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Debug|x64 = Debug|x64
Release|Win32 = Release|Win32
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{16E74A53-972D-4762-BC18-8946FB1EF452}.Debug|Win32.ActiveCfg = Debug|Win32
{16E74A53-972D-4762-BC18-8946FB1EF452}.Debug|Win32.Build.0 = Debug|Win32
{16E74A53-972D-4762-BC18-8946FB1EF452}.Debug|x64.ActiveCfg = Debug|x64
{16E74A53-972D-4762-BC18-8946FB1EF452}.Debug|x64.Build.0 = Debug|x64
{16E74A53-972D-4762-BC18-8946FB1EF452}.Release|Win32.ActiveCfg = Release|Win32
{16E74A53-972D-4762-BC18-8946FB1EF452}.Release|Win32.Build.0 = Release|Win32
{16E74A53-972D-4762-BC18-8946FB1EF452}.Release|x64.ActiveCfg = Release|x64
{16E74A53-972D-4762-BC18-8946FB1EF452}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View file

@ -0,0 +1,157 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{16E74A53-972D-4762-BC18-8946FB1EF452}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>stabtest</RootNamespace>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)</OutDir>
<IntDir>obj\$(Configuration)\$(Platform)</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)</OutDir>
<IntDir>obj\$(Configuration)\$(Platform)</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)</OutDir>
<IntDir>obj\$(Configuration)\$(Platform)</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)</OutDir>
<IntDir>obj\$(Configuration)\$(Platform)</IntDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\..\common\simplethread.cpp" />
<ClCompile Include="..\stabtest.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\..\atomicops.h" />
<ClInclude Include="..\..\..\readerwriterqueue.h" />
<ClInclude Include="..\..\common\simplethread.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View file

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\stabtest.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\common\simplethread.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\..\atomicops.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\readerwriterqueue.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\common\simplethread.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>

View file

@ -0,0 +1,28 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2013
VisualStudioVersion = 12.0.30501.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "stabtest", "stabtest.vcxproj", "{16E74A53-972D-4762-BC18-8946FB1EF452}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Debug|x64 = Debug|x64
Release|Win32 = Release|Win32
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{16E74A53-972D-4762-BC18-8946FB1EF452}.Debug|Win32.ActiveCfg = Debug|Win32
{16E74A53-972D-4762-BC18-8946FB1EF452}.Debug|Win32.Build.0 = Debug|Win32
{16E74A53-972D-4762-BC18-8946FB1EF452}.Debug|x64.ActiveCfg = Debug|x64
{16E74A53-972D-4762-BC18-8946FB1EF452}.Debug|x64.Build.0 = Debug|x64
{16E74A53-972D-4762-BC18-8946FB1EF452}.Release|Win32.ActiveCfg = Release|Win32
{16E74A53-972D-4762-BC18-8946FB1EF452}.Release|Win32.Build.0 = Release|Win32
{16E74A53-972D-4762-BC18-8946FB1EF452}.Release|x64.ActiveCfg = Release|x64
{16E74A53-972D-4762-BC18-8946FB1EF452}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View file

@ -0,0 +1,161 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{16E74A53-972D-4762-BC18-8946FB1EF452}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>stabtest</RootNamespace>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>v120</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>v120</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>v120</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>v120</PlatformToolset>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)</OutDir>
<IntDir>obj\$(Configuration)\$(Platform)</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)</OutDir>
<IntDir>obj\$(Configuration)\$(Platform)</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)</OutDir>
<IntDir>obj\$(Configuration)\$(Platform)</IntDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)</OutDir>
<IntDir>obj\$(Configuration)\$(Platform)</IntDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\..\common\simplethread.cpp" />
<ClCompile Include="..\stabtest.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\..\atomicops.h" />
<ClInclude Include="..\..\..\readerwriterqueue.h" />
<ClInclude Include="..\..\common\simplethread.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View file

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\stabtest.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\common\simplethread.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\..\atomicops.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\readerwriterqueue.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\common\simplethread.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>

View file

@ -0,0 +1,80 @@
#include "../../readerwriterqueue.h"
#include "../common/simplethread.h"
using namespace moodycamel;
#include <cstdlib>
#include <exception>
#include <fstream>
#include <cstdlib> // rand()
//#include <unistd.h> // usleep()
void unpredictableDelay(int extra = 0)
{
/* if ((rand() & 4095) == 0) {
usleep(2000 + extra); // in microseconds
}*/
}
int main(int argc, char** argv)
{
// Disable buffering (so that when run in, e.g., Sublime Text, the output appears as it is written)
std::setvbuf(stdout, nullptr, _IONBF, 0);
std::printf("Running stability test for moodycamel::ReaderWriterQueue.\n");
std::printf("Logging to 'log.txt'. Press CTRL+C to quit.\n\n");
std::ofstream log("log.txt");
try {
for (unsigned int i = 0; true; ++i) {
log << "Test #" << i << std::endl;
std::printf("Test #%d\n", i);
ReaderWriterQueue<unsigned long long> q((rand() % 32) + 1);
SimpleThread writer([&]() {
for (unsigned long long j = 0; j < 1024ULL * 1024ULL * 32ULL; ++j) {
unpredictableDelay(500);
q.enqueue(j);
}
});
SimpleThread reader([&]() {
bool canLog = true;
unsigned long long element;
for (unsigned long long j = 0; j < 1024ULL * 1024ULL * 32ULL;) {
if (canLog && (j & (1024 * 1024 * 16 - 1)) == 0) {
log << " ... iteration " << j << std::endl;
std::printf(" ... iteration %llu\n", j);
canLog = false;
}
unpredictableDelay();
if (q.try_dequeue(element)) {
if (element != j) {
log << " ERROR DETECTED: Expected to read " << j << " but found " << element << std::endl;
std::printf(" ERROR DETECTED: Expected to read %llu but found %llu", j, element);
}
++j;
canLog = true;
}
}
if (q.try_dequeue(element)) {
log << " ERROR DETECTED: Expected queue to be empty" << std::endl;
std::printf(" ERROR DETECTED: Expected queue to be empty\n");
}
});
writer.join();
reader.join();
}
}
catch (std::exception const& ex) {
log << " ERROR DETECTED: Exception thrown: " << ex.what() << std::endl;
std::printf(" ERROR DETECTED: Exception thrown: %s\n", ex.what());
}
return 0;
}

View file

@ -0,0 +1,27 @@
ifeq ($(OS),Windows_NT)
EXT=.exe
PLATFORM_OPTS=-static
PLATFORM_LD_OPTS=-Wl,--no-as-needed
else
UNAME_S := $(shell uname -s)
ifeq ($(UNAME_S),Darwin)
EXT=
PLATFORM_OPTS=
PLATFORM_LD_OPTS=
else
EXT=
PLATFORM_OPTS=
PLATFORM_LD_OPTS=-lrt -Wl,--no-as-needed
endif
endif
default: unittests$(EXT)
unittests$(EXT): unittests.cpp ../../readerwriterqueue.h ../../readerwritercircularbuffer.h ../../atomicops.h ../common/simplethread.h ../common/simplethread.cpp minitest.h makefile
g++ $(PLATFORM_OPTS) -std=c++11 -Wsign-conversion -Wpedantic -Wall -DNDEBUG -O3 -g unittests.cpp ../common/simplethread.cpp -o unittests$(EXT) -pthread $(PLATFORM_LD_OPTS)
run: unittests$(EXT)
./unittests$(EXT)

View file

@ -0,0 +1,125 @@
// ©2013-2014 Cameron Desrochers.
// Distributed under the simplified BSD license (see the LICENSE file that
// should have come with this header).
// Provides an extremely basic unit testing framework.
#pragma once
#include <cstdio>
#include <string>
#include <map>
#include <vector>
#include <type_traits>
#include <typeinfo>
#ifdef __GNUG__
#include <cxxabi.h>
#include <cstdlib>
#endif
#define REGISTER_TEST(testName) registerTest(#testName, &subclass_t::testName)
#define ASSERT_OR_FAIL(expr) { if (!(expr)) { notifyTestFailed(__LINE__, #expr); return false; } }
#define SUCCEED() { return true; }
// Uses CRTP
template<typename TSubclass>
class TestClass
{
public:
static void notifyTestFailed(int line, const char* expr)
{
std::printf(" FAILED!\n ******* Assertion failed (line %d): %s\n\n", line, expr);
}
bool validateTestName(std::string const& which) const
{
return testMap.find(which) != testMap.end();
}
void getAllTestNames(std::vector<std::string>& names) const
{
for (auto it = testMap.cbegin(); it != testMap.cend(); ++it) {
names.push_back(it->first);
}
}
bool run(unsigned int iterations = 1)
{
bool success = true;
for (auto it = testVec.cbegin(); it != testVec.cend(); ++it) {
if (!execTest(*it, iterations)) {
success = false;
}
}
return success;
}
bool run(std::vector<std::string> const& which, unsigned int iterations = 1)
{
bool success = true;
for (auto it = which.begin(); it != which.end(); ++it) {
if (!execTest(*testMap.find(*it), iterations)) {
success = false;
}
}
return success;
}
protected:
typedef TSubclass subclass_t;
void registerTest(const char* name, bool (subclass_t::* method)())
{
testVec.push_back(std::make_pair(std::string(name), method));
testMap[std::string(name)] = method;
}
bool execTest(std::pair<std::string, bool (subclass_t::*)()> const& testRef, unsigned int iterations)
{
std::printf("%s::%s... \n", demangle_type_name(typeid(subclass_t).name()).c_str(), testRef.first.c_str());
bool result = true;
for (unsigned int i = 0; i != iterations; ++i) {
if (!(static_cast<subclass_t*>(this)->*testRef.second)()) {
result = false;
break;
}
}
if (result) {
std::printf(" passed\n\n");
}
else {
std::printf(" FAILED!\n\n");
}
return result;
}
private:
static std::string demangle_type_name(const char* name)
{
#ifdef __GNUG__
// Adapted from http://stackoverflow.com/a/4541470/21475
int status = -4;
char* res = abi::__cxa_demangle(name, nullptr, nullptr, &status);
const char* const demangled_name = (status == 0) ? res : name;
std::string ret(demangled_name);
std::free(res);
return ret;
#else
return name;
#endif
}
protected:
std::vector<std::pair<std::string, bool (TSubclass::*)()> > testVec;
std::map<std::string, bool (TSubclass::*)()> testMap;
};

View file

@ -0,0 +1,26 @@

Microsoft Visual Studio Solution File, Format Version 11.00
# Visual Studio 2010
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "unittests", "unittests.vcxproj", "{C209657D-56BF-4A61-8FD2-DBAEB1E51B3B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Debug|x64 = Debug|x64
Release|Win32 = Release|Win32
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{C209657D-56BF-4A61-8FD2-DBAEB1E51B3B}.Debug|Win32.ActiveCfg = Debug|Win32
{C209657D-56BF-4A61-8FD2-DBAEB1E51B3B}.Debug|Win32.Build.0 = Debug|Win32
{C209657D-56BF-4A61-8FD2-DBAEB1E51B3B}.Debug|x64.ActiveCfg = Debug|x64
{C209657D-56BF-4A61-8FD2-DBAEB1E51B3B}.Debug|x64.Build.0 = Debug|x64
{C209657D-56BF-4A61-8FD2-DBAEB1E51B3B}.Release|Win32.ActiveCfg = Release|Win32
{C209657D-56BF-4A61-8FD2-DBAEB1E51B3B}.Release|Win32.Build.0 = Release|Win32
{C209657D-56BF-4A61-8FD2-DBAEB1E51B3B}.Release|x64.ActiveCfg = Release|x64
{C209657D-56BF-4A61-8FD2-DBAEB1E51B3B}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View file

@ -0,0 +1,158 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{C209657D-56BF-4A61-8FD2-DBAEB1E51B3B}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>unittests</RootNamespace>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
<IntDir>obj\$(Configuration)\$(Platform)\</IntDir>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)\</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<IntDir>obj\$(Configuration)\$(Platform)\</IntDir>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)\</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<IntDir>obj\$(Configuration)\$(Platform)\</IntDir>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)\</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<IntDir>obj\$(Configuration)\$(Platform)\</IntDir>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)\</OutDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="..\..\..\atomicops.h" />
<ClInclude Include="..\..\..\readerwriterqueue.h" />
<ClInclude Include="..\..\common\simplethread.h" />
<ClInclude Include="..\minitest.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\common\simplethread.cpp" />
<ClCompile Include="..\unittests.cpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View file

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\..\readerwriterqueue.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\atomicops.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\common\simplethread.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\minitest.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\common\simplethread.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\unittests.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

View file

@ -0,0 +1,28 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2013
VisualStudioVersion = 12.0.30501.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "unittests", "unittests.vcxproj", "{C209657D-56BF-4A61-8FD2-DBAEB1E51B3B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Debug|x64 = Debug|x64
Release|Win32 = Release|Win32
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{C209657D-56BF-4A61-8FD2-DBAEB1E51B3B}.Debug|Win32.ActiveCfg = Debug|Win32
{C209657D-56BF-4A61-8FD2-DBAEB1E51B3B}.Debug|Win32.Build.0 = Debug|Win32
{C209657D-56BF-4A61-8FD2-DBAEB1E51B3B}.Debug|x64.ActiveCfg = Debug|x64
{C209657D-56BF-4A61-8FD2-DBAEB1E51B3B}.Debug|x64.Build.0 = Debug|x64
{C209657D-56BF-4A61-8FD2-DBAEB1E51B3B}.Release|Win32.ActiveCfg = Release|Win32
{C209657D-56BF-4A61-8FD2-DBAEB1E51B3B}.Release|Win32.Build.0 = Release|Win32
{C209657D-56BF-4A61-8FD2-DBAEB1E51B3B}.Release|x64.ActiveCfg = Release|x64
{C209657D-56BF-4A61-8FD2-DBAEB1E51B3B}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View file

@ -0,0 +1,162 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{C209657D-56BF-4A61-8FD2-DBAEB1E51B3B}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>unittests</RootNamespace>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>v120</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>v120</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>v120</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<PlatformToolset>v120</PlatformToolset>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
<IntDir>obj\$(Configuration)\$(Platform)\</IntDir>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)\</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<IntDir>obj\$(Configuration)\$(Platform)\</IntDir>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)\</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<IntDir>obj\$(Configuration)\$(Platform)\</IntDir>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)\</OutDir>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<IntDir>obj\$(Configuration)\$(Platform)\</IntDir>
<OutDir>$(SolutionDir)$(Configuration)\$(Platform)\</OutDir>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<PrecompiledHeader>
</PrecompiledHeader>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<PrecompiledHeader>
</PrecompiledHeader>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="..\..\..\atomicops.h" />
<ClInclude Include="..\..\..\readerwriterqueue.h" />
<ClInclude Include="..\..\common\simplethread.h" />
<ClInclude Include="..\minitest.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\common\simplethread.cpp" />
<ClCompile Include="..\unittests.cpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View file

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\..\readerwriterqueue.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\..\atomicops.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\..\common\simplethread.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\minitest.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\common\simplethread.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\unittests.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

View file

@ -0,0 +1,951 @@
// ©2013-2015 Cameron Desrochers
// Unit tests for moodycamel::ReaderWriterQueue
#include <cstdio>
#include <cstdio>
#include <cstring>
#include <string>
#include <memory>
#include "minitest.h"
#include "../common/simplethread.h"
#include "../../readerwriterqueue.h"
#include "../../readerwritercircularbuffer.h"
using namespace moodycamel;
// *NOT* thread-safe
struct Foo
{
Foo() : copied(false) { id = _id()++; }
Foo(Foo const& other) : id(other.id), copied(true) { }
Foo(Foo&& other) : id(other.id), copied(other.copied) { other.copied = true; }
Foo& operator=(Foo&& other)
{
verify();
id = other.id, copied = other.copied;
other.copied = true;
return *this;
}
~Foo() { verify(); }
private:
void verify()
{
if (copied) return;
if (id != _last_destroyed_id() + 1) {
_destroyed_in_order() = false;
}
_last_destroyed_id() = id;
++_destroy_count();
}
public:
static void reset() { _destroy_count() = 0; _id() = 0; _destroyed_in_order() = true; _last_destroyed_id() = -1; }
static int destroy_count() { return _destroy_count(); }
static bool destroyed_in_order() { return _destroyed_in_order(); }
private:
static int& _destroy_count() { static int c = 0; return c; }
static int& _id() { static int i = 0; return i; }
static bool& _destroyed_in_order() { static bool d = true; return d; }
static int& _last_destroyed_id() { static int i = -1; return i; }
int id;
bool copied;
};
#if MOODYCAMEL_HAS_EMPLACE
class UniquePtrWrapper
{
public:
UniquePtrWrapper() = default;
UniquePtrWrapper(std::unique_ptr<int> p) : m_p(std::move(p)) {}
int get_value() const { return *m_p; }
std::unique_ptr<int>& get_ptr() { return m_p; }
private:
std::unique_ptr<int> m_p;
};
#endif
/// Extracted from private static method of ReaderWriterQueue
static size_t ceilToPow2(size_t x)
{
// From http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
--x;
x |= x >> 1;
x |= x >> 2;
x |= x >> 4;
for (size_t i = 1; i < sizeof(size_t); i <<= 1) {
x |= x >> (i << 3);
}
++x;
return x;
}
class ReaderWriterQueueTests : public TestClass<ReaderWriterQueueTests>
{
public:
ReaderWriterQueueTests()
{
REGISTER_TEST(create_empty_queue);
REGISTER_TEST(enqueue_one);
REGISTER_TEST(enqueue_many);
REGISTER_TEST(nonempty_destroy);
REGISTER_TEST(try_enqueue);
REGISTER_TEST(try_dequeue);
REGISTER_TEST(peek);
REGISTER_TEST(pop);
REGISTER_TEST(size_approx);
REGISTER_TEST(max_capacity);
REGISTER_TEST(threaded);
REGISTER_TEST(blocking);
REGISTER_TEST(vector);
#if MOODYCAMEL_HAS_EMPLACE
REGISTER_TEST(emplace);
REGISTER_TEST(try_enqueue_fail_workaround);
REGISTER_TEST(try_emplace_fail);
#endif
REGISTER_TEST(blocking_circular_buffer);
}
bool create_empty_queue()
{
{
ReaderWriterQueue<int> q;
}
{
ReaderWriterQueue<int> q(1234);
}
return true;
}
bool enqueue_one()
{
int item;
{
item = 0;
ReaderWriterQueue<int> q(1);
q.enqueue(12345);
ASSERT_OR_FAIL(q.try_dequeue(item));
ASSERT_OR_FAIL(item == 12345);
}
{
item = 0;
ReaderWriterQueue<int> q(1);
ASSERT_OR_FAIL(q.try_enqueue(12345));
ASSERT_OR_FAIL(q.try_dequeue(item));
ASSERT_OR_FAIL(item == 12345);
}
return true;
}
bool enqueue_many()
{
int item = -1;
{
ReaderWriterQueue<int> q(100);
for (int i = 0; i != 100; ++i) {
q.enqueue(i);
}
for (int i = 0; i != 100; ++i) {
ASSERT_OR_FAIL(q.try_dequeue(item));
ASSERT_OR_FAIL(item == i);
}
}
{
ReaderWriterQueue<int> q(100);
for (int i = 0; i != 1200; ++i) {
q.enqueue(i);
}
for (int i = 0; i != 1200; ++i) {
ASSERT_OR_FAIL(q.try_dequeue(item));
ASSERT_OR_FAIL(item == i);
}
}
return true;
}
bool nonempty_destroy()
{
// Some elements at beginning
Foo::reset();
{
ReaderWriterQueue<Foo> q(31);
for (int i = 0; i != 10; ++i) {
q.enqueue(Foo());
}
ASSERT_OR_FAIL(Foo::destroy_count() == 0);
}
ASSERT_OR_FAIL(Foo::destroy_count() == 10);
ASSERT_OR_FAIL(Foo::destroyed_in_order());
// Entire block
Foo::reset();
{
ReaderWriterQueue<Foo> q(31);
for (int i = 0; i != 31; ++i) {
q.enqueue(Foo());
}
ASSERT_OR_FAIL(Foo::destroy_count() == 0);
}
ASSERT_OR_FAIL(Foo::destroy_count() == 31);
ASSERT_OR_FAIL(Foo::destroyed_in_order());
// Multiple blocks
Foo::reset();
{
ReaderWriterQueue<Foo> q(31);
for (int i = 0; i != 94; ++i) {
q.enqueue(Foo());
}
ASSERT_OR_FAIL(Foo::destroy_count() == 0);
}
ASSERT_OR_FAIL(Foo::destroy_count() == 94);
ASSERT_OR_FAIL(Foo::destroyed_in_order());
// Some elements in another block
Foo::reset();
{
ReaderWriterQueue<Foo> q(31);
Foo item;
for (int i = 0; i != 42; ++i) {
q.enqueue(Foo());
}
ASSERT_OR_FAIL(Foo::destroy_count() == 0);
for (int i = 0; i != 31; ++i) {
ASSERT_OR_FAIL(q.try_dequeue(item));
}
ASSERT_OR_FAIL(Foo::destroy_count() == 31);
}
ASSERT_OR_FAIL(Foo::destroy_count() == 43);
ASSERT_OR_FAIL(Foo::destroyed_in_order());
// Some elements in multiple blocks
Foo::reset();
{
ReaderWriterQueue<Foo> q(31);
Foo item;
for (int i = 0; i != 123; ++i) {
q.enqueue(Foo());
}
for (int i = 0; i != 25; ++i) {
ASSERT_OR_FAIL(q.try_dequeue(item));
}
for (int i = 0; i != 47; ++i) {
q.enqueue(Foo());
}
for (int i = 0; i != 140; ++i) {
ASSERT_OR_FAIL(q.try_dequeue(item));
}
for (int i = 0; i != 230; ++i) {
q.enqueue(Foo());
}
for (int i = 0; i != 130; ++i) {
ASSERT_OR_FAIL(q.try_dequeue(item));
}
for (int i = 0; i != 100; ++i) {
q.enqueue(Foo());
}
}
ASSERT_OR_FAIL(Foo::destroy_count() == 501);
ASSERT_OR_FAIL(Foo::destroyed_in_order());
return true;
}
bool try_enqueue()
{
ReaderWriterQueue<int> q(31);
int item;
int size = 0;
for (int i = 0; i < 10000; ++i) {
if ((rand() & 1) == 1) {
bool result = q.try_enqueue(i);
if (size == 31) {
ASSERT_OR_FAIL(!result);
}
else {
ASSERT_OR_FAIL(result);
++size;
}
}
else {
bool result = q.try_dequeue(item);
if (size == 0) {
ASSERT_OR_FAIL(!result);
}
else {
ASSERT_OR_FAIL(result);
--size;
}
}
}
return true;
}
bool try_dequeue()
{
int item;
{
ReaderWriterQueue<int> q(1);
ASSERT_OR_FAIL(!q.try_dequeue(item));
}
{
ReaderWriterQueue<int, 2> q(10);
ASSERT_OR_FAIL(!q.try_dequeue(item));
}
return true;
}
bool threaded()
{
weak_atomic<int> result;
result = 1;
ReaderWriterQueue<int> q(100);
SimpleThread reader([&]() {
int item;
int prevItem = -1;
for (int i = 0; i != 1000000; ++i) {
if (q.try_dequeue(item)) {
if (item <= prevItem) {
result = 0;
}
prevItem = item;
}
}
});
SimpleThread writer([&]() {
for (int i = 0; i != 1000000; ++i) {
if (((i >> 7) & 1) == 0) {
q.enqueue(i);
}
else {
q.try_enqueue(i);
}
}
});
writer.join();
reader.join();
return result.load() == 1 ? true : false;
}
bool peek()
{
weak_atomic<int> result;
result = 1;
ReaderWriterQueue<int> q(100);
SimpleThread reader([&]() {
int item;
int prevItem = -1;
int* peeked;
for (int i = 0; i != 100000; ++i) {
peeked = q.peek();
if (peeked != nullptr) {
if (q.try_dequeue(item)) {
if (item <= prevItem || item != *peeked) {
result = 0;
}
prevItem = item;
}
else {
result = 0;
}
}
}
});
SimpleThread writer([&]() {
for (int i = 0; i != 100000; ++i) {
if (((i >> 7) & 1) == 0) {
q.enqueue(i);
}
else {
q.try_enqueue(i);
}
}
});
writer.join();
reader.join();
return result.load() == 1 ? true : false;
}
bool pop()
{
weak_atomic<int> result;
result = 1;
ReaderWriterQueue<int> q(100);
SimpleThread reader([&]() {
int item;
int prevItem = -1;
int* peeked;
for (int i = 0; i != 100000; ++i) {
peeked = q.peek();
if (peeked != nullptr) {
item = *peeked;
if (q.pop()) {
if (item <= prevItem) {
result = 0;
}
prevItem = item;
}
else {
result = 0;
}
}
}
});
SimpleThread writer([&]() {
for (int i = 0; i != 100000; ++i) {
if (((i >> 7) & 1) == 0) {
q.enqueue(i);
}
else {
q.try_enqueue(i);
}
}
});
writer.join();
reader.join();
return result.load() == 1 ? true : false;
}
bool size_approx()
{
weak_atomic<int> result;
weak_atomic<int> front;
weak_atomic<int> tail;
result = 1;
front = 0;
tail = 0;
ReaderWriterQueue<int> q(10);
SimpleThread reader([&]() {
int item;
for (int i = 0; i != 100000; ++i) {
if (q.try_dequeue(item)) {
fence(memory_order_release);
front = front.load() + 1;
}
int size = static_cast<int>(q.size_approx());
fence(memory_order_acquire);
int tail_ = tail.load();
int front_ = front.load();
if (size > tail_ - front_ || size < 0) {
result = 0;
}
}
});
SimpleThread writer([&]() {
for (int i = 0; i != 100000; ++i) {
tail = tail.load() + 1;
fence(memory_order_release);
q.enqueue(i);
int tail_ = tail.load();
int front_ = front.load();
fence(memory_order_acquire);
int size = static_cast<int>(q.size_approx());
if (size > tail_ - front_ || size < 0) {
result = 0;
}
}
});
writer.join();
reader.join();
return result.load() == 1 ? true : false;
}
bool max_capacity()
{
{
// this math for queue size estimation is only valid for q_size <= 256
for (size_t q_size = 2; q_size < 256; ++q_size) {
ReaderWriterQueue<size_t> q(q_size);
ASSERT_OR_FAIL(q.max_capacity() == ceilToPow2(q_size+1)-1);
const size_t start_cap = q.max_capacity();
for (size_t i = 0; i < start_cap+1; ++i) // fill 1 past capacity to resize
q.enqueue(i);
ASSERT_OR_FAIL(q.max_capacity() == 3*start_cap+1);
}
}
return true;
}
bool blocking()
{
{
BlockingReaderWriterQueue<int> q;
int item;
q.enqueue(123);
ASSERT_OR_FAIL(q.try_dequeue(item));
ASSERT_OR_FAIL(item == 123);
ASSERT_OR_FAIL(q.size_approx() == 0);
q.enqueue(234);
ASSERT_OR_FAIL(q.size_approx() == 1);
ASSERT_OR_FAIL(*q.peek() == 234);
ASSERT_OR_FAIL(*q.peek() == 234);
ASSERT_OR_FAIL(q.pop());
ASSERT_OR_FAIL(q.try_enqueue(345));
q.wait_dequeue(item);
ASSERT_OR_FAIL(item == 345);
ASSERT_OR_FAIL(!q.peek());
ASSERT_OR_FAIL(q.size_approx() == 0);
ASSERT_OR_FAIL(!q.try_dequeue(item));
}
weak_atomic<int> result;
result = 1;
{
BlockingReaderWriterQueue<int> q(100);
SimpleThread reader([&]() {
int item = -1;
int prevItem = -1;
for (int i = 0; i != 1000000; ++i) {
q.wait_dequeue(item);
if (item <= prevItem) {
result = 0;
}
prevItem = item;
}
});
SimpleThread writer([&]() {
for (int i = 0; i != 1000000; ++i) {
q.enqueue(i);
}
});
writer.join();
reader.join();
ASSERT_OR_FAIL(q.size_approx() == 0);
ASSERT_OR_FAIL(result.load());
}
{
BlockingReaderWriterQueue<int> q(100);
SimpleThread reader([&]() {
int item = -1;
int prevItem = -1;
for (int i = 0; i != 1000000; ++i) {
if (!q.wait_dequeue_timed(item, 1000)) {
--i;
continue;
}
if (item <= prevItem) {
result = 0;
}
prevItem = item;
}
});
SimpleThread writer([&]() {
for (int i = 0; i != 1000000; ++i) {
q.enqueue(i);
for (volatile int x = 0; x != 100; ++x);
}
});
writer.join();
reader.join();
int item;
ASSERT_OR_FAIL(q.size_approx() == 0);
ASSERT_OR_FAIL(!q.wait_dequeue_timed(item, 0));
ASSERT_OR_FAIL(!q.wait_dequeue_timed(item, 1));
ASSERT_OR_FAIL(result.load());
}
#if MOODYCAMEL_HAS_EMPLACE
{
BlockingReaderWriterQueue<UniquePtrWrapper> q(100);
std::unique_ptr<int> p { new int(123) };
q.emplace(std::move(p));
q.try_emplace(std::move(p));
UniquePtrWrapper item;
ASSERT_OR_FAIL(q.wait_dequeue_timed(item, 0));
ASSERT_OR_FAIL(item.get_value() == 123);
ASSERT_OR_FAIL(q.wait_dequeue_timed(item, 0));
ASSERT_OR_FAIL(item.get_ptr() == nullptr);
ASSERT_OR_FAIL(q.size_approx() == 0);
}
#endif
return true;
}
bool vector()
{
{
std::vector<ReaderWriterQueue<int>> queues;
queues.push_back(ReaderWriterQueue<int>());
queues.emplace_back();
queues[0].enqueue(1);
queues[1].enqueue(2);
std::swap(queues[0], queues[1]);
int item;
ASSERT_OR_FAIL(queues[0].try_dequeue(item));
ASSERT_OR_FAIL(item == 2);
ASSERT_OR_FAIL(queues[1].try_dequeue(item));
ASSERT_OR_FAIL(item == 1);
}
{
std::vector<BlockingReaderWriterQueue<int>> queues;
queues.push_back(BlockingReaderWriterQueue<int>());
queues.emplace_back();
queues[0].enqueue(1);
queues[1].enqueue(2);
std::swap(queues[0], queues[1]);
int item;
ASSERT_OR_FAIL(queues[0].try_dequeue(item));
ASSERT_OR_FAIL(item == 2);
queues[1].wait_dequeue(item);
ASSERT_OR_FAIL(item == 1);
}
return true;
}
#if MOODYCAMEL_HAS_EMPLACE
bool emplace()
{
ReaderWriterQueue<UniquePtrWrapper> q(100);
std::unique_ptr<int> p { new int(123) };
q.emplace(std::move(p));
UniquePtrWrapper item;
ASSERT_OR_FAIL(q.try_dequeue(item));
ASSERT_OR_FAIL(item.get_value() == 123);
ASSERT_OR_FAIL(q.size_approx() == 0);
return true;
}
// This is what you have to do to try_enqueue() a movable type, and demonstrates why try_emplace() is useful
bool try_enqueue_fail_workaround()
{
ReaderWriterQueue<UniquePtrWrapper> q(0);
{
// A failed try_enqueue() will still delete p
std::unique_ptr<int> p { new int(123) };
q.try_enqueue(std::move(p));
ASSERT_OR_FAIL(q.size_approx() == 0);
ASSERT_OR_FAIL(p == nullptr);
}
{
// Workaround isn't pretty and potentially expensive - use try_emplace() instead
std::unique_ptr<int> p { new int(123) };
UniquePtrWrapper w(std::move(p));
q.try_enqueue(std::move(w));
p = std::move(w.get_ptr());
ASSERT_OR_FAIL(q.size_approx() == 0);
ASSERT_OR_FAIL(p != nullptr);
ASSERT_OR_FAIL(*p == 123);
}
return true;
}
bool try_emplace_fail()
{
ReaderWriterQueue<UniquePtrWrapper> q(0);
std::unique_ptr<int> p { new int(123) };
q.try_emplace(std::move(p));
ASSERT_OR_FAIL(q.size_approx() == 0);
ASSERT_OR_FAIL(p != nullptr);
ASSERT_OR_FAIL(*p == 123);
return true;
}
#endif
bool blocking_circular_buffer()
{
{
// Basic enqueue
BlockingReaderWriterCircularBuffer<int> q(65);
for (int iteration = 0; iteration != 128; ++iteration) { // check there's no problem with mismatch between nominal and allocated capacity
ASSERT_OR_FAIL(q.max_capacity() == 65);
ASSERT_OR_FAIL(q.size_approx() == 0);
ASSERT_OR_FAIL(!q.try_pop());
ASSERT_OR_FAIL(q.try_enqueue(0));
ASSERT_OR_FAIL(q.max_capacity() == 65);
ASSERT_OR_FAIL(q.size_approx() == 1);
ASSERT_OR_FAIL(*q.peek() == 0);
for (int i = 1; i != 65; ++i)
q.wait_enqueue(i);
ASSERT_OR_FAIL(q.size_approx() == 65);
ASSERT_OR_FAIL(!q.try_enqueue(65));
// Basic dequeue
int item;
ASSERT_OR_FAIL(q.try_dequeue(item));
ASSERT_OR_FAIL(item == 0);
for (int i = 1; i != 65; ++i) {
q.wait_dequeue(item);
ASSERT_OR_FAIL(item == i);
}
ASSERT_OR_FAIL(!q.try_dequeue(item));
ASSERT_OR_FAIL(!q.wait_dequeue_timed(item, 1));
ASSERT_OR_FAIL(item == 64);
}
}
{
// Zero capacity
BlockingReaderWriterCircularBuffer<int> q(0);
ASSERT_OR_FAIL(q.max_capacity() == 0);
ASSERT_OR_FAIL(!q.try_enqueue(1));
ASSERT_OR_FAIL(!q.wait_enqueue_timed(1, 0));
}
// Element lifetimes
Foo::reset();
{
BlockingReaderWriterCircularBuffer<Foo> q(31);
{
Foo item;
for (int i = 0; i != 23 + 32; ++i) {
ASSERT_OR_FAIL(q.try_enqueue(Foo()));
ASSERT_OR_FAIL(q.try_dequeue(item));
}
ASSERT_OR_FAIL(Foo::destroy_count() == 23 + 32);
ASSERT_OR_FAIL(Foo::destroyed_in_order());
}
Foo::reset();
{
Foo item;
for (int i = 0; i != 23 + 32; ++i) {
ASSERT_OR_FAIL(q.try_enqueue(Foo()));
item = std::move(*q.peek());
ASSERT_OR_FAIL(q.try_pop());
}
ASSERT_OR_FAIL(!q.peek());
ASSERT_OR_FAIL(!q.try_pop());
ASSERT_OR_FAIL(Foo::destroy_count() == 23 + 32);
ASSERT_OR_FAIL(Foo::destroyed_in_order());
}
Foo::reset();
{
Foo item;
for (int i = 0; i != 10; ++i)
ASSERT_OR_FAIL(q.try_enqueue(Foo()));
ASSERT_OR_FAIL(q.size_approx() == 10);
ASSERT_OR_FAIL(Foo::destroy_count() == 0);
ASSERT_OR_FAIL(q.try_dequeue(item));
ASSERT_OR_FAIL(q.size_approx() == 9);
ASSERT_OR_FAIL(Foo::destroy_count() == 1);
}
ASSERT_OR_FAIL(Foo::destroy_count() == 2);
ASSERT_OR_FAIL(Foo::destroyed_in_order());
BlockingReaderWriterCircularBuffer<Foo> q2(std::move(q));
ASSERT_OR_FAIL(q.size_approx() == 0);
ASSERT_OR_FAIL(q2.size_approx() == 9);
BlockingReaderWriterCircularBuffer<Foo> q3(2);
q3 = std::move(q2);
ASSERT_OR_FAIL(q.size_approx() == 0);
ASSERT_OR_FAIL(q2.size_approx() == 0);
ASSERT_OR_FAIL(q3.size_approx() == 9);
q = std::move(q2);
ASSERT_OR_FAIL(q.size_approx() == 0);
ASSERT_OR_FAIL(q2.size_approx() == 0);
ASSERT_OR_FAIL(q3.size_approx() == 9);
ASSERT_OR_FAIL(Foo::destroy_count() == 2);
}
ASSERT_OR_FAIL(Foo::destroy_count() == 11);
ASSERT_OR_FAIL(Foo::destroyed_in_order());
weak_atomic<int> result;
result = 1;
{
// Threaded
BlockingReaderWriterCircularBuffer<int> q(8);
SimpleThread reader([&]() {
int item;
for (int i = 0; i != 1000000; ++i) {
int* peeked = q.peek();
if (peeked) {
item = *peeked;
if (peeked != q.peek() || !q.try_pop())
result = 0;
}
else {
q.wait_dequeue(item);
}
if (item != i)
result = 0;
}
});
SimpleThread writer([&]() {
for (int i = 0; i != 1000000; ++i)
q.wait_enqueue(i);
});
writer.join();
reader.join();
ASSERT_OR_FAIL(q.size_approx() == 0);
ASSERT_OR_FAIL(result.load());
}
return true;
}
};
void printTests(ReaderWriterQueueTests const& tests)
{
std::printf(" Supported tests are:\n");
std::vector<std::string> names;
tests.getAllTestNames(names);
for (auto it = names.cbegin(); it != names.cend(); ++it) {
std::printf(" %s\n", it->c_str());
}
}
// Basic test harness
int main(int argc, char** argv)
{
bool disablePrompt = false;
std::vector<std::string> selectedTests;
// Disable buffering (so that when run in, e.g., Sublime Text, the output appears as it is written)
std::setvbuf(stdout, nullptr, _IONBF, 0);
// Isolate the executable name
std::string progName = argv[0];
auto slash = progName.find_last_of("/\\");
if (slash != std::string::npos) {
progName = progName.substr(slash + 1);
}
ReaderWriterQueueTests tests;
// Parse command line options
if (argc == 1) {
std::printf("Running all unit tests for moodycamel::ReaderWriterQueue.\n(Run %s --help for other options.)\n\n", progName.c_str());
}
else {
bool printHelp = false;
bool printedTests = false;
bool error = false;
for (int i = 1; i < argc; ++i) {
if (std::strcmp(argv[i], "--help") == 0) {
printHelp = true;
}
else if (std::strcmp(argv[i], "--disable-prompt") == 0) {
disablePrompt = true;
}
else if (std::strcmp(argv[i], "--run") == 0) {
if (i + 1 == argc || argv[i + 1][0] == '-') {
std::printf("Expected test name argument for --run option.\n");
if (!printedTests) {
printTests(tests);
printedTests = true;
}
error = true;
continue;
}
if (!tests.validateTestName(argv[++i])) {
std::printf("Unrecognized test '%s'.\n", argv[i]);
if (!printedTests) {
printTests(tests);
printedTests = true;
}
error = true;
continue;
}
selectedTests.push_back(argv[i]);
}
else {
std::printf("Unrecognized option '%s'.\n", argv[i]);
error = true;
}
}
if (error || printHelp) {
if (error) {
std::printf("\n");
}
std::printf("%s\n Description: Runs unit tests for moodycamel::ReaderWriterQueue\n", progName.c_str());
std::printf(" --help Prints this help blurb\n");
std::printf(" --run test Runs only the specified test(s)\n");
std::printf(" --disable-prompt Disables prompt before exit when the tests finish\n");
return error ? -1 : 0;
}
}
int exitCode = 0;
bool result;
if (selectedTests.size() > 0) {
result = tests.run(selectedTests);
}
else {
result = tests.run();
}
if (result) {
std::printf("All %stests passed.\n", (selectedTests.size() > 0 ? "selected " : ""));
}
else {
std::printf("Test(s) failed!\n");
exitCode = 2;
}
if (!disablePrompt) {
std::printf("Press ENTER to exit.\n");
getchar();
}
return exitCode;
}

1524
External/reaper-plugins/reaper_plugin.h vendored Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,31 @@
#ifndef _REAPER_VST3_INTERFACES_H_
#define _REAPER_VST3_INTERFACES_H_
class IReaperHostApplication : public FUnknown // available from IHostApplication in REAPER v5.02+
{
public:
// Gets a REAPER Extension API function by name, returns NULL is failed
virtual void* PLUGIN_API getReaperApi(CStringA funcname) = 0;
virtual void* PLUGIN_API getReaperParent(uint32 w) = 0; // get parent track(=1), take(=2), project(=3), fxdsp(=4), trackchan(=5)
// Multi-purpose function, returns NULL if unsupported
virtual void* PLUGIN_API reaperExtended(uint32 call, void *parm1, void *parm2, void *parm3) = 0;
static const FUID iid;
};
DECLARE_CLASS_IID (IReaperHostApplication, 0x79655E36, 0x77EE4267, 0xA573FEF7, 0x4912C27C)
class IReaperUIEmbedInterface : public FUnknown // supported by REAPER v6.24+, queried from plug-in IEditController
{
public:
// note: VST2 uses CanDo "hasCockosEmbeddedUI"==0xbeef0000, then opcode=effVendorSpecific, index=effEditDraw, opt=(float)msg, value=parm2, ptr=parm3
// see reaper_plugin_fx_embed.h
virtual Steinberg::TPtrInt embed_message(int msg, Steinberg::TPtrInt parm2, Steinberg::TPtrInt parm3) = 0;
static const FUID iid;
};
DECLARE_CLASS_IID (IReaperUIEmbedInterface, 0x049bf9e7, 0xbc74ead0, 0xc4101e86, 0x7f725981)
#endif

1470
External/reaper-plugins/swell-types.h vendored Normal file

File diff suppressed because it is too large Load diff