Commit graph

343 commits

Author SHA1 Message Date
Wim Taymans
818d1435ce treewide: access the position information using helpers
Make sure we don't access out of bounds and that we use the helpers
wherever we can to access the position information.
2025-10-21 13:06:25 +02:00
Wim Taymans
8bbca3b8f3 spa: add spa_audio_parse_position_n
Add a function that accepts the size of the position array when reading
the audio positions. This makes it possible to decouple the position
array size from SPA_AUDIO_MAX_CHANNELS.

Also use SPA_N_ELEMENTS to pass the number of array elements to
functions instead of a fixed constant. This makes it easier to change
the array size later to a different constant without having to patch up
all the places where the size is used.
2025-10-21 09:59:13 +02:00
Wim Taymans
13b8c23767 Don't use SPA_AUDIO_MAX_CHANNELS directly
Make a MAX_CHANNELS define and use that one in code. This makes it
easier to change the constant later.
2025-10-21 09:43:06 +02:00
Carlos Rafael Giani
14b242c737 node-driver: Make sure the discont clock flag does not remain set forever
If the timer was canceled, the discont flag needs to be set. But in the
next cycle, unless the timer was canceled again, that flag should not
remain set.
2025-07-18 10:52:13 +02:00
Carlos Rafael Giani
bb022c1b84 node-driver: Handle realtime clock modifications
If the user alters the realtime clock (for example by using the "date"
command in the shell), and the node driver uses the realtime clock as
the timerfd clock, then the scheduled graph cycle invocation may not
take place, or may take place much later than planned, because the
timestamp that was passed to spa_system_timerfd_settime() is now invalid.
Configure the timer to automatically be canceled if the realtime clock
is modified so that the graph cycle can be rescheduled with an updated
timestamp that is actually usable with the altered realtime clock.
2025-07-17 13:13:34 +00:00
Wim Taymans
f93b3b23a3 loop: fix use after free case
Because we can now destroy sources (and free the source structure) by
simply holding the lock, there is a window where we might access the
freed source.

When we in iterate release the lock and go into the epoll, another
thread might acquire the lock and delete the fd from epoll. This might
happen right after epoll detected activity on the fd. When iterate
manages to acquire the lock again, it will process to dispatch the
active fd and deref the ep.data pointer, which is now pointing to freed
memory.

Fix this by incrementing a removed_count whenever we remove a source.
Check the counter if it was the same as before the epoll otherwise we
can't assume all sources are alive still. Return in that case as if
there were no fds to poll. The caller should reenter the iterate at some
point and we will return all the fds with activity, minus the one that
got destroyed. We need to give control to the caller because part of the
removal could be to stop the loop iteration all together.
2025-06-30 12:44:15 +02:00
Wim Taymans
9a6f8d31dc loop: unlock the lock when blocking on invoke
When we are the owners of the loop lock and we are not in the loop
thread itself, release all locks so that the loop can start processing
our invoke items and we get a chance to make progress. After that
re-acquire the locks.

This can happen when you change some of the core loop_locked() calls to
blocking _invoke functions that are called with the loop locked.

We have all core blocking invoke functions removed now so this is not
actually going to be used but just in case an application tries to
blocking invoke while locking the loop, this will now at least do
something else than deadlock.
2025-06-26 14:23:36 +02:00
Wim Taymans
8e32afb863 loop: don't call the hooks around blocking wait
The hooks were previously used to unlock the loop but now that the
lock is handled inside the loop itself and we don't unlock before the
blocking read anymore, we should also not call the hooks.

The blocking invoke function is not meant to be called with any of the
loop context locks acquired in order to avoid a deadlock. Make this (and
other blocking risks) clear in the documentation.

See #4472
2025-06-10 11:57:38 +02:00
Arun Raghavan
420c510d47 spa: loop: Fix potential uninitialised result 2025-06-06 12:50:56 +05:30
Arun Raghavan
56c6e19f99 Revert "spa: loop: Change get_time() timeout to unsigned"
This reverts commit c515f9bf8e. The PW
APIs use int64_t (partly because SPA_NSEC_PER_SEC is an LL), and we
don't want to change already public API.
2025-06-03 15:20:15 +05:30
Arun Raghavan
c515f9bf8e spa: loop: Change get_time() timeout to unsigned
A signed value doesn't really make sense in this context, so let's keep
it unsigned so the semantics are clear. This does break the interface,
but should be okay since it's not in a release yet.
2025-06-03 09:39:30 +00:00
Wim Taymans
34796d5bb8 loop: keep a free_list of sources
When a source is destroyed, move it to a free_list and reuse the memory
when a new source is made. This way we can avoid doing a free() from
the epoll thread.
2025-05-29 10:17:16 +02:00
Wim Taymans
f2452a6af7 spa: some more invoke -> locked calls 2025-05-29 10:17:16 +02:00
Wim Taymans
f7fdafc203 loop: add method to run a function with the lock
Convert some _invoke to _locked
2025-05-29 10:17:16 +02:00
Wim Taymans
fb49e0795c loop: move thread-loop to support loop
Add more synchronization primitives to spa loop so that we can replace
the thread-loop with it.
2025-05-29 10:17:16 +02:00
Wim Taymans
65cbbf1a02 spa: add locking to the loop
We can add a PTHREAD_PRIO_INHERIT lock to the loop to protect the
callbacks and then use this to update shared data in an RT-safe way.

This can avoid some invoke calls that require a context switch but
also due to the nature of epoll cause locking in the kernel with non-RT
guarantees.

Because we use PRIO_INHERIT, the code executed in the lock must not use
any RT-unsafe functions.
2025-05-29 10:17:16 +02:00
Pauli Virtanen
1ec814977c logger: support logging local timestamps and other timestamp logs
In timestamps, support different clocks and local time as formats.

Local real time timestamps are useful when trying to correlate logs from
different sources.
2025-01-13 13:48:22 +00:00
Wim Taymans
a4b553f3d4 spa: serialize in_thread flushes with a mutex
When we have no thread running the loop, we need to flush the queues
from the invoking thread. Make sure that when multiple threads attempt
this that we serialize the flushing because the flushing code is not
thread safe.
2024-12-03 16:38:28 +01:00
Wim Taymans
8c59fae42d loop: add overflow queues again
Add the overflow queues again. We can easily iterate atomically over the
overflow queues and flush them.

Overflowing a queue is quite common when heavy swapping is done and
should never cause a lockup, so allocate new queues as we need them. We
can share the eventfd with the main queue to avoid wastings fds.

The limit on the number of queues is then only for when concurrent
threads want to invoke things, so 128 is plenty enough.
2024-11-14 17:38:43 +01:00
Wim Taymans
6cf320e387 loop: handle queue overflow better
When a queue overflows we place the queue back in the stack and try
again. Because it's at the top of the stack we take exactly the same
queue and keep on looping forever if the other thread is blocked for
some reason.

Instead, mark the queue as overflowed and only place it back in the
stack when we have flushed it.

This avoids a deadlock when the main-thread invokes on the data loop
and blocks and when the data loop invokes on the main-thread and
overflows the queue.
2024-11-14 15:58:09 +01:00
Wim Taymans
9c19284f7f support: make the loop queue handling lockfree
Don't use TSS to store per-thread queues but keep a lockfree stack of
queues. We can then pick off a queue and write to that one and place it
back after use.

We need to keep the queues indexed by id in the stack because otherwise
we would need to compare-and-swap 128 bits (pointer + tag), which is
more problematic.

Because we keep the queues in an array and no queue is ever removed and
the array can only grow, we can quite easily just iterate the array
without a lock. Without the lock we also fix one of the potential
problems with ardour where the queue_flush thread is canceled while
flushing and the queue_mutex remains locked.

Because we end up with all queues in the array now, we can overflow the
fixed max amount of queues we can manage. When that happens, sleep for a
while and try again. This is a case where more than QUEUES_MAX (128) threads
are invoking at the same time and is rather unlikely.

There is also the queue overflow case which we now also must handle with
a retry. This potentially uses more eventfds but again this should be
unlikely and cause no further problems.

See #4356
2024-11-04 17:41:14 +01:00
Wim Taymans
22c45af7e0 loop: refcount the queues
The loop in the TSS gets an extra refcount and is unreffed when the TSS
destroy is called.

We can then also ref the queue during the function callback. When the
queue (thread) was destroyed during the callback, ignore the result and
continue with the next queues.

See #4356
2024-10-21 17:47:31 +02:00
Wim Taymans
bb53aa08ad loop: warn when some queues are still in TSS
When we clear we need to have all our queues removed from the TSS when
we delete the tss key or else they are leaked, check an warn about this
using a refcount of queued in the TSS.

See #4356
2024-10-21 17:08:10 +02:00
Wim Taymans
c4fece74a5 loop: fix race in shutdown
Make it possible to call loop_queue_destroy() from both the TSS destroy
and impl_clear() without races. We make sure that only one can remove
the queue from the queue list and cleanup. We also store the IN_TSS flag
in the flags so that we can see them before the queue is added to the
queue list. Only free the IN_TSS queue when the TSS destroy is called.

See #4356
2024-10-21 16:45:17 +02:00
Wim Taymans
dca11e6c41 loop: remove extra allocation
We don't actually need the extra allocation for the tss. We can just
mark the queue as being in the tss. When a queue is destroyed, mark it
as destroyed but when it is still in the tss, don't free the structure
yet. We free the structure when we destroy the tss.

We can also free the overflow queues of a queue when it is destroyed
immediately.
2024-10-02 09:40:54 +02:00
Wim Taymans
5d3aac313d loop: free tss from the thread calling impl::clear
The thread that calls the impl_clear method might be the main thread and
is certainly not going to call the invoke function anymore so free the
tss if there is any.

Fixes a leak in the unit test.
2024-10-02 09:21:00 +02:00
Wim Taymans
82585b7475 loop: improve tss cleanup
Store a pointer to a pointer to a queue in the tss and point to it from
the queue.

When we destroy the queue when we _clear the support, we can clear the
pointer in the tss as well. This way, when the thread is later
destroyed, it will see the NULL pointer and not try to free the queue
again.
2024-10-01 13:25:15 +02:00
Gleb Popov
50dab6dda6 meson: Search for and link to stdthreads 2024-09-23 08:09:45 +00:00
Gleb Popov
67ddfc3053 Use the 'thrd_success' constant when checking for tss_create result 2024-09-23 08:09:45 +00:00
Barnabás Pőcze
86004ba3f1 spa: support: use feature macro from config.h
Fixes 8166b9c580 ("spa/support: implement RISCV V CPU detection")
2024-09-18 22:29:11 +02:00
sunyuechi
8166b9c580 spa/support: implement RISCV V CPU detection 2024-09-18 10:40:48 +00:00
Wim Taymans
e2991f6398 json: add helper function to parse channel positions
Use the helper instead of duplicating the same code.

Also add some helpers to parse a json array of uint32_t

Move some functions to convert between type name and id.
2024-09-18 09:54:34 +02:00
Wim Taymans
cd81b5f39a spa: add spa_json_begin_array/object and relaxed versions
Add spa_json_begin_array/object to replace
spa_json_init+spa_json_begin_array/object

This function is better because it does not waste a useless spa_json
structure as an iterator. The relaxed versions also error out when the
container is mismatched because parsing a mismatched container is not
going to give any results anyway.
2024-09-16 09:50:33 +02:00
Wim Taymans
fff52bb7a2 Revert "spa: support: loop: do not call control hooks on blocking invoke"
This reverts commit 9ae89b4247.

All invokes should be paired with a lock/unlock if the loop requires
this. For internal calls of invoke, this will also be true because all
pipewire functions should be called with the lock.

Fixes #4215
2024-08-21 14:48:54 +02:00
Wim Taymans
8c1a69f1b5 loop: don't usleep when queue is full
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
2024-08-06 12:05:11 +02:00
Barnabás Pőcze
9ae89b4247 spa: support: loop: do not call control hooks on blocking invoke
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.
2024-08-05 18:14:39 +00:00
Wim Taymans
494600d46a loop: release queue lock before calling invoke function
We don't actually need to hold the lock while calling the invoke
function, we only need the lock to protect the list of queues.
2024-07-30 12:04:42 +02:00
Wim Taymans
2a8a08f303 loop: signal when queue is full
When our queue is full, signal the wakeup event to make sure the thread
will wake up and try to clear the queue before we go to sleep.
2024-07-20 14:05:09 +02:00
Wim Taymans
d4515378e7 node-driver: 5 seconds of freewheel timeout is enough
We retry to run the graph every 5 seconds in case it didn't complete. A
10 seconds timeout feels quite long.
2024-07-12 12:25:18 +02:00
Wim Taymans
42096de3cc node: add a clock XRUN_RECOVER flag
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).
2024-07-12 12:21:59 +02:00
David Coles
9ae9009edf Define setlinebuf for MSVC
According to `setbuf(3)`, `setlinebuf` is exactly equivalent to
`setvbuf(stream, NULL, _IOLBF, 0)`.
2024-07-01 15:28:58 +00:00
David Coles
2770e96e08 loop: fix update_timer handling of solo repeat argument
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.
2024-06-30 18:37:49 +00:00
Wim Taymans
1ae4374ccf Fix compilation with -Werror=float-conversion
Better make the conversions explicit so that we don't get any surprises.

Fixes #4065
2024-06-18 12:17:56 +02:00
Wim Taymans
8b23a8a89e loop: flush items in the order they were added
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.
2024-05-08 12:21:54 +02:00
Wim Taymans
8ff40e6252 loop: improve in_thread handling of invoke queue
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.
2024-04-29 15:56:00 +02:00
Wim Taymans
de0db48f17 loop: create a per-thread queue
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
2024-04-29 15:17:45 +02:00
Wim Taymans
c76424da36 loop: move invoke queue to separate object
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.
2024-04-29 12:10:48 +02:00
Pauli Virtanen
ac35ecf329 journal: prepend code location to messages at debug log levels
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.
2024-04-28 16:02:28 +03:00
Barnabás Pőcze
6a26e6dd3f treewide: fix some format string issues
Use the proper specifier, and cast to a known type where the type
is not guaranteed by any standard.

See #3975
2024-04-25 07:24:10 +00:00
Wim Taymans
af310523db spa: improve null-audio-sink channels
We can just update the channels in the props, we don't need an extra
property that can go out of sync with the channels.

See #3931
2024-03-28 17:11:16 +01:00