When the queue is full, before this patch we used to go into usleep in
the hope that the other thread will run and empty the queue and that we
can retry after the usleep.
This however does not always work because the other thread might be waiting
for the thread that does the invoke call and we lock forever.
Therefore we should always try to make progress in some way. Instead of
waiting, allocate an (or use the previously allocated) overflow queue and
write to that one. We can chain multiple overflow queues together as many
as we need (but we might want to bound that as well).
The loop.retry-timeout property is now deprecated.
See #4114
The control hooks of a loop are called before the loop starts polling
and after it has finished polling. Currently, this is used to implement
the locking in pw_thread_loop. This is used to guarantee that the thread
loop's lock is taken while the thread loop is dispatching, and that
the lock can be taken while the loop is polling, when it is running
no user-space code.
However, calling the thread control hooks of thread A when doing an
blocking invoke from thread B serves little purpose, and in fact
can cause issues: for example, issuing a blocking invoke on a
pw_thread_loop does not work unless the lock thereof is taken.
This behaviour, of calling the control hooks from other threads,
is also not documented, and goes contrary to what is currently
stated in the loop.h header file:
/** Executed right before waiting for events. It is typically used to
* release locks. */
...
/** Executed right after waiting for events. It is typically used to
* reacquire locks. */
At the moment the implementation allows any thread to queue invoke
items on any other thread without restrictions; calling the control
hooks only places extra restrictions on the usability of this mechanism
(in case of pw_thread_loop, having to take the loop's lock).
So do not call the control hooks when doing a blocking invoke.
Make a new flag that is set when the process function is called because
of a recover from a graph xrun.
Use this flag in the freewheel driver to detect a recover and to avoid
scheduling a new timeout. We should schedule a new timeout only when the
process function was called after completion.
This fixes export in ardour some more when the initial driver timeout
didn't complete (when, for example, some nodes were still starting up).
I believe the intent here is that if a `interval` is provided
but `value` is unset, then `value` should default to `period`
so the timer first fires after one `interval`.
Since `interval` is always a relative duration, `value` should
be interpreted as a relative duration, not an absolute one.
Add a count to each invoke item that is updated with an increasing
loop atomic counter. Flush items from the queues based on their count
so that items are flushed in the order they were added even if they
were added to different queues.
Because we now have a dedicated queue per thread, we can simply add our
invoke item to the queue and then flush all the queues when we are
running in the thread of the loop.
This simplifies some things and removes potential out-of-order messages
that got queued while flushing.
Keep a thread local queue. This makes it possible for multiple threads
to write to the ringbuffer.
There is a lock to protect the list of queues. It can only be contended
when new queues are created in the threads but this can be done at
thread startup.
Fixes#3983
Make an internal queue object that implements the invoke queue.
Because we can not do invokes concurrently from different threads, this
is required to make per-thread invoke queues later.
Debug and trace log messages are often written based on the stderr
logging, where code location is always visible.
journalctl does not show the code location without extra tricks,
which makes user-submitted debug logs from journal more cryptic.
Make journal log more similar to stderr logs by prepending the code
location and log level in the log message when the log topic
level is >= DEBUG.
When timer is not using monotonic clock, apply clock offset to translate
the time values to the monotonic clock when putting them to spa_io_clock
nsec fields.
Get appropriate clock offset by smoothed filtering. The parameters here
keep the offset jitter < 10ns or so.
As monotonic/boottime/realtime all contain adjtime(), there generally is
no drift in the offset here, so just averaging should be fine.
Also fix using wrong timer clock when freewheeling.
Propagate the error if spa_system_eventfd_create() fails. Also copy
errno before calling spa_log_debug() in spa_system_eventfd_create() to
make sure it is not overwritten.
When the invoke ringbuffer is full, sleep a little and try again.
Add an option to set the retty timeout, setting this to 0 restores
the old behaviour of returning -EPIPE.
Most callers don't check the return values and might assume the invoke
call is queued or executed, which could cause crashes or leaks.
When the queue overruns, it's better to log a warning and hope that the
problem is resolved soon. We might abort or return the error to the
caller later if we want to break the retry loop.
See !1887
Move some of the tracking code for the DLL to where it is used.
Add resync.ms (default 10) option at which we give up rate adjusting
and instead do a hard resync. This results in a jump in the position
of the graph clock.
When freewheeling we will immediately schedule a new graph cycle when we
get a process call because the graph completed.
When the process call is not done, because of some xrun or
because some node was removed that causes the graph to fail completion,
The next cycle will happen after a timeout.
This timeout was calculated as the ideal wakeup time (after a quantum of
time) and would accumulate for each timeout. The result is that the
timeout ended up far in the future and would stall the freewheel driver
for a long time.
Fix this by always setting the next timeout to wakeup time + freewheel.timeout
seconds. Also add a config property for the timeout (10 seconds, like
jack2 by default).
Make sure the log level on the chained logger is the same as ours.
Makes PIPEWIRE_DEBUG=3 make run print debug again.
This used to work because the log level was parsed and set before the
loggers were created and chained, and so they all got the same level.
Now that the level can be changed with metadata at runtime, we can't
really update all past loggers so let the journal logger copy the
level itself.
When we don't have the thread id yet, don't add the pollfds yet
but wait until we do our first wait operation.
Use flags for eventfd. We can use this to communicate between all kinds
of threads with read/write.
Use evl_init() in the init function, don't attach the main loop, just
the thread that dos the first poll.
The user may not know which is the active PHC index of a bonded
interface. We can now specify the interface name instead of a device
as the clock.interface property and query the interface about the
active PHC index.
Make sure that the position only advances in the running state.
When we are not following a clock we can simply increment the position
with the duration every time we run.
If we are following a clock. Take the elapsed time of the clock into
account when aligning to the position.
Fixes#3189