On 8/13/2026 12:04 AM, Wolfgang Gaggl via Pd-list wrote:
I have been thinking: how could anyone implement synchronization
primitives without reliable atomic operations? To me, a bug in the
pthread_rwlock implementation seems more likely.
Generally, it all seems to work, including atomics.
I believe there's an issue with the GCC implementation for pthread_rwlock on
this platform; as far I can observe only with the try_to_lock shared_lock.
There are some mentioned differences (occasionally having to try_to_lock
multiple times to get a lock on an uncontested lock) that I can also see on the
native MIPSPro compiler.
But at times it fails to get a reader lock with try_to_lock when another
scoped_shared_lock is active which looks like a bug.
I observed this only in GCC. Since the native MIPSPro compiler supports only
C++98 I didn't fully test the exact scenario with the specific timing there.
Since you pointed out that sink.cpp may have concurrent writers active during
the stream I chose another way to do this.
std::atomic<uint32_t> process_active_{0};
std::atomic<uint32_t> writer_gate_{0};
In source_desc::process():
// Writers set writer_gate_ and wait for process_active_ == 0 before
mutating
// This prevents writer/process overlap without using rwlock try_lock in
audio path.
if (writer_gate_.load(std::memory_order_acquire) != 0) {
if (stream_state_ != stream_state::inactive) {
LOG_DEBUG("AooSink: process blocked by writer gate");
add_xrun(1);
}
}
// process() sets process_active_ to to set writer block
sink_process_activity_guard process_guard(process_active_);
// For the extra-cautious, recheck after announcing active status to avoid
race condition.
if (writer_gate_.load(std::memory_order_acquire) != 0) {
if (stream_state_ != stream_state::inactive) {
LOG_DEBUG("AooSink: process blocked by writer gate");
add_xrun(1);
}
}
// now do the processing stuff...
}
For all writer calls:
// Writer gate guard: blocks new process() entries while a writer is
pending.
sink_writer_gate_guard _writer_gate(writer_gate_);
// This function loops pause_cpu() while process() is active
wait_for_sink_process_idle(process_active_);
// do write stuff...
This looks overly complicated to me. I can't even tell if it's correct
without looking at the implementation of sink_process_activity_guard,
sink_writer_gate_guard and wait_for_sink_process_idle. In general, I
would advice against ad-hoc solutions with atomics. If possible, stick
to standard synchronization patterns.
(BTW, that additional writer_gate_.load(std::memory_order_acquire) is
essential because otherwise you run into the ABA problem.)
As I said, you just need to replace the shared_mutex with a
shared_spinlock. Apart from that you can revert back to the original
code. (The process() method does a try-lock, but because it's now a
simple CAS operation there is no need for a retry-loop. By default, the
readers and writers would do full spinlocks, but you can replace them
with try-lock loops that occasionally sleep for a short time to fight
priority inversion.)
Christof
This method does not block in the process() thread and avoids using the
problematic try_to_lock.
It also uses fewer CPU cycles than looping try_to_lock in process().
As you suggested, I tested it by changing format parameters during the running
stream in quick succession to see if I can break it, but it all works without
issues.
---
[email protected] - the Pure Data mailinglist
https://lists.iem.at/hyperkitty/list/[email protected]/message/6323HU2BWSBL2R4RPCZBSRS3XBLWW3P5/
To unsubscribe send an email [email protected] mailing list
UNSUBSCRIBE and account-management ->https://lists.iem.at/
---
[email protected] - the Pure Data mailinglist
https://lists.iem.at/hyperkitty/list/[email protected]/message/T6TL6VSZEPVPZF55H36XAVWY4FJ22RF3/
To unsubscribe send an email to [email protected] mailing list
UNSUBSCRIBE and account-management -> https://lists.iem.at/