Browse Source

Add fallback thread to Engine to step blocks if no primary module is set.

tags/v2.0.0
Andrew Belt 3 years ago
parent
commit
b17f6ee3fe
3 changed files with 85 additions and 11 deletions
  1. +2
    -0
      adapters/standalone.cpp
  2. +5
    -0
      include/engine/Engine.hpp
  3. +78
    -11
      src/engine/Engine.cpp

+ 2
- 0
adapters/standalone.cpp View File

@@ -214,6 +214,8 @@ int main(int argc, char* argv[]) {
APP->patch->launch(patchPath);
}

APP->engine->startFallbackThread();

// Run context
if (settings::headless) {
printf("Press enter to exit.\n");


+ 5
- 0
include/engine/Engine.hpp View File

@@ -40,6 +40,7 @@ struct Engine {
Write-locks.
*/
void setPrimaryModule(Module* module);
INTERNAL void setPrimaryModule_NoLock(Module* module);
Module* getPrimaryModule();

/** Returns the sample rate used by the engine for stepping each module.
@@ -223,6 +224,10 @@ struct Engine {
Write-locks.
*/
void fromJson(json_t* rootJ);

/** If no primary module is set, the fallback Engine thread will step blocks, using the CPU clock for timing.
*/
void startFallbackThread();
};




+ 78
- 11
src/engine/Engine.cpp View File

@@ -279,7 +279,13 @@ struct Engine::Internal {
HybridBarrier engineBarrier;
HybridBarrier workerBarrier;
std::atomic<int> workerModuleIndex;
// For worker threads
Context* context;

bool fallbackRunning = false;
std::thread fallbackThread;
std::mutex fallbackMutex;
std::condition_variable fallbackCv;
};


@@ -514,7 +520,19 @@ Engine::Engine() {


Engine::~Engine() {
// Stop fallback thread if running
{
std::lock_guard<std::mutex> lock(internal->fallbackMutex);
internal->fallbackRunning = false;
internal->fallbackCv.notify_all();
}
if (internal->fallbackThread.joinable())
internal->fallbackThread.join();

// Shut down workers
Engine_relaunchWorkers(this, 0);

// Clear modules, cables, etc
clear();

// Make sure there are no cables or modules in the rack on destruction.
@@ -617,6 +635,13 @@ void Engine::setPrimaryModule(Module* module) {
if (module == internal->primaryModule)
return;
WriteLock lock(internal->mutex);
setPrimaryModule_NoLock(module);
}


void Engine::setPrimaryModule_NoLock(Module* module) {
if (module == internal->primaryModule)
return;

if (internal->primaryModule) {
// Dispatch UnsetPrimaryEvent
@@ -631,6 +656,11 @@ void Engine::setPrimaryModule(Module* module) {
Module::SetPrimaryEvent e;
internal->primaryModule->onSetPrimary(e);
}

// Wake up fallback thread if primary module was unset
if (!internal->primaryModule) {
internal->fallbackCv.notify_all();
}
}


@@ -795,15 +825,6 @@ void Engine::removeModule_NoLock(Module* module) {
// Check that the module actually exists
auto it = std::find(internal->modules.begin(), internal->modules.end(), module);
assert(it != internal->modules.end());
// If a param is being smoothed on this module, stop smoothing it immediately
if (module == internal->smoothModule) {
internal->smoothModule = NULL;
}
// Check that all cables are disconnected
for (Cable* cable : internal->cables) {
assert(cable->inputModule != module);
assert(cable->outputModule != module);
}
// Dispatch RemoveEvent
Module::RemoveEvent eRemove;
module->onRemove(eRemove);
@@ -813,8 +834,17 @@ void Engine::removeModule_NoLock(Module* module) {
paramHandle->module = NULL;
}
// Unset primary module
if (internal->primaryModule == module) {
internal->primaryModule = NULL;
if (getPrimaryModule() == module) {
setPrimaryModule_NoLock(NULL);
}
// If a param is being smoothed on this module, stop smoothing it immediately
if (module == internal->smoothModule) {
internal->smoothModule = NULL;
}
// Check that all cables are disconnected
for (Cable* cable : internal->cables) {
assert(cable->inputModule != module);
assert(cable->outputModule != module);
}
// Update expanders of other modules
for (Module* m : internal->modules) {
@@ -1309,5 +1339,42 @@ void EngineWorker::run() {
}


static void Engine_fallbackRun(Engine* that) {
system::setThreadName("Engine fallback");
contextSet(that->internal->context);

while (that->internal->fallbackRunning) {
if (!that->getPrimaryModule()) {
// Step blocks and wait
double start = system::getTime();
int frames = std::floor(that->getSampleRate() / 60);
that->stepBlock(frames);
double end = system::getTime();

double duration = frames * that->getSampleTime() - (end - start);
if (duration > 0.0) {
std::this_thread::sleep_for(std::chrono::duration<double>(duration));
}
}
else {
// Wait for primary module to be unset, or for the request to stop running
std::unique_lock<std::mutex> lock(that->internal->fallbackMutex);
that->internal->fallbackCv.wait(lock, [&]() {
return !that->internal->fallbackRunning || !that->getPrimaryModule();
});
}
}
}


void Engine::startFallbackThread() {
if (internal->fallbackThread.joinable())
return;

internal->fallbackRunning = true;
internal->fallbackThread = std::thread(Engine_fallbackRun, this);
}


} // namespace engine
} // namespace rack

Loading…
Cancel
Save