#include "splitter.h" #include namespace satdump { namespace ndsp { template SplitterBlock::SplitterBlock() : Block(std::is_same_v ? "splitter_cc" : "splitter_ff", {{"in", getTypeSampleType()}}, {/*{"out", std::is_same_v ? DSP_SAMPLE_TYPE_CF32 : DSP_SAMPLE_TYPE_F32}*/}) { } template SplitterBlock::~SplitterBlock() { } template bool SplitterBlock::work() { DSPBuffer iblk = inputs[0].fifo->wait_dequeue(); if (iblk.isTerminator()) { if (iblk.terminatorShouldPropagate()) { // Snapshot outputs under lock to avoid deadlock: // wait_enqueue() can block if FIFO is full, and del_output() // also tries to acquire vfos_mtx → would deadlock. std::vector outputs_snapshot; { std::lock_guard lock(vfos_mtx); outputs_snapshot = outputs; } // Enqueue terminators WITHOUT holding vfos_mtx for (auto &o : outputs_snapshot) { IOInfo *i = ((IOInfo *)o.blkdata.get()); if (i->forward_terminator) o.fifo->wait_enqueue(o.fifo->newBufferTerminator()); } } inputs[0].fifo->free(iblk); return true; } // Snapshot outputs under lock, then release before blocking enqueue. // This prevents deadlock when del_output()/add_output() is called // while a FIFO output is full and wait_enqueue() would block. std::vector outputs_snapshot; { std::lock_guard lock(vfos_mtx); outputs_snapshot = outputs; } // Enqueue data WITHOUT holding vfos_mtx for (auto &o : outputs_snapshot) { DSPBuffer oblk = o.fifo->newBufferSamples(iblk.max_size, sizeof(T)); memcpy(oblk.getSamples(), iblk.getSamples(), iblk.size * sizeof(T)); oblk.size = iblk.size; o.fifo->wait_enqueue(oblk); } inputs[0].fifo->free(iblk); return false; } template class SplitterBlock; template class SplitterBlock; template class SplitterBlock; template class SplitterBlock; template class SplitterBlock; } // namespace ndsp } // namespace satdump