>> 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 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 to [email protected] mailing list
UNSUBSCRIBE and account-management -> https://lists.iem.at/

Reply via email to