Skip to content

Commit ff0c7bf

Browse files
committed
Added rtcheck options with disabled/enable/relaxed modes
1 parent 9f7bbb9 commit ff0c7bf

File tree

8 files changed

+118
-29
lines changed

8 files changed

+118
-29
lines changed

CMakeLists.txt

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ if (APPLE)
1010
# Uncomment to produce a universal binary
1111
# set(CMAKE_OSX_ARCHITECTURES arm64 x86_64)
1212
set(PLUGINVAL_ENABLE_RTCHECK ON)
13+
elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux")
14+
set(PLUGINVAL_ENABLE_RTCHECK ON)
1315
endif()
1416

1517
# sanitizer options, from https://github.com/sudara/cmake-includes/blob/main/Sanitizers.cmake
@@ -42,19 +44,21 @@ endif ()
4244
set_property(GLOBAL PROPERTY USE_FOLDERS YES)
4345
option(JUCE_ENABLE_MODULE_SOURCE_GROUPS "Enable Module Source Groups" ON)
4446

47+
Include(FetchContent)
48+
4549
if(PLUGINVAL_ENABLE_RTCHECK)
46-
Include(FetchContent)
4750
FetchContent_Declare(rtcheck
48-
GIT_REPOSITORY https://github.com/Tracktion/rtcheck.git
51+
GIT_REPOSITORY git@github.com:Tracktion/rtcheck.git
4952
GIT_TAG origin/main
5053
SOURCE_DIR ${CMAKE_CURRENT_BINARY_DIR}/rtcheck)
5154
FetchContent_MakeAvailable(rtcheck)
52-
53-
# execute_process(COMMAND cmake --build ${CMAKE_CURRENT_BINARY_DIR}/rtcheck-build)
54-
# execute_process(COMMAND cmake -B ${CMAKE_CURRENT_BINARY_DIR}/rtcheck/build -S ${CMAKE_CURRENT_BINARY_DIR}/rtcheck)
55-
# execute_process(COMMAND cmake --build ${CMAKE_CURRENT_BINARY_DIR}/rtcheck)
5655
endif()
5756

57+
FetchContent_Declare(magic_enum
58+
GIT_REPOSITORY [email protected]:Neargye/magic_enum.git)
59+
FetchContent_MakeAvailable(magic_enum)
60+
61+
5862
option(PLUGINVAL_FETCH_JUCE "Fetch JUCE along with pluginval" ON)
5963

6064
if(PLUGINVAL_FETCH_JUCE)
@@ -77,7 +81,7 @@ juce_add_gui_app(pluginval
7781

7882
juce_generate_juce_header(pluginval)
7983

80-
target_compile_features(pluginval PRIVATE cxx_std_17)
84+
target_compile_features(pluginval PRIVATE cxx_std_20)
8185

8286
set_target_properties(pluginval PROPERTIES
8387
C_VISIBILITY_PRESET hidden
@@ -131,7 +135,8 @@ target_link_libraries(pluginval PRIVATE
131135
juce::juce_audio_devices
132136
juce::juce_audio_processors
133137
juce::juce_audio_utils
134-
juce::juce_recommended_warning_flags)
138+
juce::juce_recommended_warning_flags
139+
magic_enum)
135140

136141
if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
137142
target_link_libraries(pluginval PRIVATE

Source/CommandLine.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include <unistd.h>
2323
#endif
2424

25+
#include <magic_enum/magic_enum.hpp>
2526

2627
//==============================================================================
2728
static void exitWithError (const juce::String& error)
@@ -285,6 +286,7 @@ static Option possibleOptions[] =
285286
{ "--sample-rates", true },
286287
{ "--block-sizes", true },
287288
{ "--vst3validator", true },
289+
{ "--rtcheck", false },
288290
};
289291

290292
static juce::StringArray mergeEnvironmentVariables (juce::StringArray args, std::function<juce::String (const juce::String& name, const juce::String& defaultValue)> environmentVariableProvider = [] (const juce::String& name, const juce::String& defaultValue) { return juce::SystemStats::getEnvironmentVariable (name, defaultValue); })
@@ -362,6 +364,10 @@ static juce::String getHelpMessage()
362364
<< " Sets a timout which will stop validation with an error if no output from any" << newLine
363365
<< " test has happened for this number of ms." << newLine
364366
<< " By default this is 30s but can be set to \"-1\" (must be quoted) to never timeout." << newLine
367+
<< " --rtcheck [empty, disabled, enabled or relaxed]" << newLine
368+
<< " Turns on real-time saftey checks using rtcheck (macOS and Linux only)." << newLine
369+
<< " relaxed mode doesn't run the checks for the first processing block as a lot of plugins" << newLine
370+
<< " use this to allocate or initialise thread-locals (which can allocate)" << newLine
365371
<< newLine
366372
// repeating tests
367373
<< " --repeat [num repeats]" << newLine
@@ -552,6 +558,8 @@ std::pair<juce::String, PluginTests::Options> parseCommandLine (const juce::Argu
552558
options.sampleRates = getSampleRates (args);
553559
options.blockSizes = getBlockSizes (args);
554560
options.vst3Validator = getOptionValue (args, "--vst3validator", "", "Expected a path for the --vst3validator option");
561+
options.realtimeCheck = magic_enum::enum_cast<RealtimeCheck> (getOptionValue (args, "--rtcheck", "", "Expected one of [disabled, enabled, relaxed]").toString().toStdString())
562+
.value_or (RealtimeCheck::disabled);
555563

556564
return { fileOrID, options };
557565
}
@@ -622,6 +630,12 @@ juce::StringArray createCommandLine (juce::String fileOrID, PluginTests::Options
622630
if (options.vst3Validator != juce::File())
623631
args.addArray ({ "--vst3validator", options.vst3Validator.getFullPathName().quoted() });
624632

633+
if (auto rtCheckMode = options.realtimeCheck;
634+
rtCheckMode != RealtimeCheck::disabled)
635+
{
636+
args.addArray ({ "--rtcheck", std::string (magic_enum::enum_name (rtCheckMode)) });
637+
}
638+
625639
args.addArray ({ "--validate", fileOrID });
626640

627641
return args;

Source/MainComponent.cpp

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
==============================================================================*/
1414

1515
#include "MainComponent.h"
16+
17+
#include <magic_enum/magic_enum.hpp>
18+
1619
#include "PluginTests.h"
1720

1821
//==============================================================================
@@ -114,6 +117,18 @@ namespace
114117
return getAppPreferences().getValue ("vst3validator", juce::String());
115118
}
116119

120+
void setRealtimeCheckMode (RealtimeCheck rt)
121+
{
122+
getAppPreferences().setValue ("realtimeCheckMode", juce::String (std::string (magic_enum::enum_name (rt))));
123+
}
124+
125+
RealtimeCheck getRealtimeCheckMode()
126+
{
127+
auto modeString = getAppPreferences().getValue ("realtimeCheckMode", juce::String());
128+
return magic_enum::enum_cast<RealtimeCheck> (modeString.toStdString())
129+
.value_or (RealtimeCheck::disabled);
130+
}
131+
117132
PluginTests::Options getTestOptions()
118133
{
119134
PluginTests::Options options;
@@ -127,6 +142,7 @@ namespace
127142
options.sampleRates = getSampleRates();
128143
options.blockSizes = getBlockSizes();
129144
options.vst3Validator = getVST3Validator();
145+
options.realtimeCheck = getRealtimeCheckMode();
130146

131147
return options;
132148
}
@@ -336,10 +352,25 @@ MainComponent::MainComponent (Validator& v)
336352
randomise,
337353
chooseOutputDir,
338354
showVST3Validator,
339-
showSettingsDir
355+
showSettingsDir,
356+
rtCheck
340357
};
341358

342359
juce::PopupMenu m;
360+
361+
{
362+
juce::PopupMenu rtCheckMenu;
363+
364+
for (auto currentMode = getRealtimeCheckMode();
365+
auto mode : magic_enum::enum_values<RealtimeCheck>())
366+
{
367+
rtCheckMenu.addItem (getDisplayString (mode), true, mode == currentMode,
368+
[newMode = mode] { setRealtimeCheckMode (newMode); });
369+
}
370+
371+
m.addSubMenu ("Realtime check mode", rtCheckMenu);
372+
}
373+
343374
m.addItem (validateInProcess, TRANS("Validate in process"), true, getValidateInProcess());
344375
m.addItem (showRandomSeed, TRANS("Set random seed (123)").replace ("123", "0x" + juce::String::toHexString (getRandomSeed()) + "/" + juce::String (getRandomSeed())));
345376
m.addItem (showTimeout, TRANS("Set timeout (123ms)").replace ("123",juce::String (getTimeoutMs())));

Source/PluginTests.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,18 @@
1616
#include "TestUtilities.h"
1717
#include <random>
1818

19+
juce::String getDisplayString (RealtimeCheck rtc)
20+
{
21+
if (rtc == RealtimeCheck::disabled)
22+
return "Disabled (don't check for real-time safety)";
23+
24+
if (rtc == RealtimeCheck::enabled)
25+
return "Enabled (check for real-time safety in all process calls)";
26+
27+
if (rtc == RealtimeCheck::relaxed)
28+
return "Relaxed (check for real-time safety in all but the first process call)";
29+
}
30+
1931
namespace
2032
{
2133
/** Deletes a plugin asyncronously on the message thread */

Source/PluginTests.h

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,17 @@
1616

1717
#include "juce_audio_processors/juce_audio_processors.h"
1818

19+
/** Determines the type of real-time checking to perform. */
20+
enum class RealtimeCheck
21+
{
22+
disabled, ///< Doesn't check for realtime safety
23+
enabled, ///< Checks for realtime safety
24+
relaxed ///< Checks for realtime safety after the first audio callback (to allow initialisation)
25+
};
26+
27+
juce::String getDisplayString (RealtimeCheck);
28+
29+
1930
//==============================================================================
2031
/**
2132
The juce::UnitTest which will create the plugins and run each of the registered tests on them.
@@ -26,20 +37,21 @@ struct PluginTests : public juce::UnitTest
2637
/** A set of options to use when running tests. */
2738
struct Options
2839
{
29-
int strictnessLevel = 5; /**< Max test level to run. */
30-
juce::int64 randomSeed = 0; /**< The seed to use for the tests, 0 signifies a randomly generated seed. */
31-
juce::int64 timeoutMs = 30000; /**< Timeout after which to kill the test. */
32-
bool verbose = false; /**< Whether or not to log additional information. */
33-
int numRepeats = 1; /**< The number of times to repeat the tests. */
34-
bool randomiseTestOrder = false; /**< Whether to randomise the order of the tests in each repeat. */
35-
bool withGUI = true; /**< Whether or not avoid tests that instantiate a gui. */
36-
juce::File dataFile; /**< juce::File which tests can use to run user provided data. */
37-
juce::File outputDir; /**< Directory in which to write the log files for each test run. */
38-
juce::String outputFilename; /**< Filename to write logs into */
39-
juce::StringArray disabledTests; /**< List of disabled tests. */
40-
std::vector<double> sampleRates; /**< List of sample rates. */
41-
std::vector<int> blockSizes; /**< List of block sizes. */
42-
juce::File vst3Validator; /**< juce::File to use as the VST3 validator app. */
40+
int strictnessLevel = 5; /**< Max test level to run. */
41+
juce::int64 randomSeed = 0; /**< The seed to use for the tests, 0 signifies a randomly generated seed. */
42+
juce::int64 timeoutMs = 30000; /**< Timeout after which to kill the test. */
43+
bool verbose = false; /**< Whether or not to log additional information. */
44+
int numRepeats = 1; /**< The number of times to repeat the tests. */
45+
bool randomiseTestOrder = false; /**< Whether to randomise the order of the tests in each repeat. */
46+
bool withGUI = true; /**< Whether or not avoid tests that instantiate a gui. */
47+
juce::File dataFile; /**< juce::File which tests can use to run user provided data. */
48+
juce::File outputDir; /**< Directory in which to write the log files for each test run. */
49+
juce::String outputFilename; /**< Filename to write logs into */
50+
juce::StringArray disabledTests; /**< List of disabled tests. */
51+
std::vector<double> sampleRates; /**< List of sample rates. */
52+
std::vector<int> blockSizes; /**< List of block sizes. */
53+
juce::File vst3Validator; /**< juce::File to use as the VST3 validator app. */
54+
RealtimeCheck realtimeCheck = RealtimeCheck::disabled; /**< The type of real-time safety checking to perform. */
4355
};
4456

4557
/** Creates a set of tests for a fileOrIdentifier. */

Source/RTCheck.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,18 @@
2828
| static_cast<uint64_t>(rtc::check_flags::pthread_mutex_unlock)); \
2929
}
3030

31+
#define RTC_REALTIME_CONTEXT_IF_ENABLED(realtimeCheckMode, blockNum) \
32+
std::optional<rtc::realtime_context> rc; \
33+
\
34+
if (realtimeCheckMode != RealtimeCheck::disabled) \
35+
{ \
36+
if (realtimeCheckMode != RealtimeCheck::relaxed || blockNum > 0) \
37+
{ \
38+
rc.emplace(); \
39+
rtc::disable_checks_for_thread (static_cast<uint64_t>(rtc::check_flags::pthread_mutex_lock) \
40+
| static_cast<uint64_t>(rtc::check_flags::pthread_mutex_unlock)); \
41+
} \
42+
}
3143
#else
3244
#define RTC_REALTIME_CONTEXT
3345
#endif

Source/tests/BasicTests.cpp

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -157,16 +157,19 @@ struct EditorWhilstProcessingTest : public PluginTest
157157
auto processThread = std::async (std::launch::async,
158158
[&]
159159
{
160+
int blockNum = 0;
161+
160162
while (shouldProcess)
161163
{
162164
fillNoise (ab);
163165

164166
{
165-
RTC_REALTIME_CONTEXT_IF_LEVEL_10(ut.getOptions().strictnessLevel)
167+
RTC_REALTIME_CONTEXT_IF_ENABLED(ut.getOptions().realtimeCheck, blockNum)
166168
instance.processBlock (ab, mb);
167169
}
168170

169171
mb.clear();
172+
++blockNum;
170173

171174
threadStartedEvent.signal();
172175
}
@@ -241,7 +244,7 @@ struct AudioProcessingTest : public PluginTest
241244
fillNoise (ab);
242245

243246
{
244-
RTC_REALTIME_CONTEXT_IF_LEVEL_10(ut.getOptions().strictnessLevel)
247+
RTC_REALTIME_CONTEXT_IF_ENABLED(ut.getOptions().realtimeCheck, i)
245248
instance.processBlock (ab, mb);
246249
}
247250

@@ -403,7 +406,7 @@ struct AutomationTest : public PluginTest
403406
if (isPluginInstrument)
404407
addNoteOn (mb, noteChannel, noteNumber, juce::jmin (10, subBlockSize));
405408

406-
for (;;)
409+
for (int blockNum = 0;; ++blockNum)
407410
{
408411
// Set random parameter values
409412
{
@@ -430,7 +433,7 @@ struct AutomationTest : public PluginTest
430433
fillNoise (subBuffer);
431434

432435
{
433-
RTC_REALTIME_CONTEXT_IF_LEVEL_10(ut.getOptions().strictnessLevel)
436+
RTC_REALTIME_CONTEXT_IF_ENABLED(ut.getOptions().realtimeCheck, blockNum)
434437
instance.processBlock (subBuffer, mb);
435438
}
436439

@@ -682,7 +685,7 @@ struct ParameterThreadSafetyTest : public PluginTest
682685
fillNoise (ab);
683686

684687
{
685-
RTC_REALTIME_CONTEXT_IF_LEVEL_10(ut.getOptions().strictnessLevel)
688+
RTC_REALTIME_CONTEXT_IF_ENABLED(ut.getOptions().realtimeCheck, i)
686689
instance.processBlock (ab, mb);
687690
}
688691

Source/tests/ExtremeTests.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ struct AllocationsInRealTimeThreadTest : public PluginTest
6767
fillNoise (ab);
6868

6969
{
70-
RTC_REALTIME_CONTEXT_IF_LEVEL_10(ut.getOptions().strictnessLevel)
70+
RTC_REALTIME_CONTEXT_IF_ENABLED(ut.getOptions().realtimeCheck, i)
7171
ScopedAllocationDisabler sad;
7272
instance.processBlock (ab, mb);
7373
}

0 commit comments

Comments
 (0)