Commit graph

75 commits

Author SHA1 Message Date
Barnabás Pőcze
8941fc2866 spa: support: loop: get array size using macro 2022-03-06 18:40:43 +00:00
Barnabás Pőcze
cb8c2d8857 spa: support: loop: reset rmask after dispatch
Reset the `rmask` of the sources to zero after
dispatching the callbacks. This way the sources
are always as up-to-date as possible.
2022-03-06 18:40:43 +00:00
Barnabás Pőcze
a22ce76dbf spa: support: loop: initialize rmask
Set `rmask` to zero when a source is added to,
or removed from the loop.
2022-03-06 18:40:43 +00:00
Barnabás Pőcze
e2287f35db spa: support: loop: move struct members
Move the boolean members of `struct source_impl` to the end
of the struct. This changes the size of the struct from
104 bytes to 96 bytes on x86-64.

Before:

struct source_impl {
        struct spa_source          source;               /*     0    48 */
        struct impl *              impl;                 /*    48     8 */
        struct spa_list            link;                 /*    56    16 */
        /* --- cacheline 1 boundary (64 bytes) was 8 bytes ago --- */
        _Bool                      close;                /*    72     1 */

        /* XXX 7 bytes hole, try to pack */

        union {
                spa_source_io_func_t io;                 /*    80     8 */
                spa_source_idle_func_t idle;             /*    80     8 */
                spa_source_event_func_t event;           /*    80     8 */
                spa_source_timer_func_t timer;           /*    80     8 */
                spa_source_signal_func_t signal;         /*    80     8 */
        } func;                                          /*    80     8 */
        _Bool                      enabled;              /*    88     1 */

        /* XXX 7 bytes hole, try to pack */

        struct spa_source *        fallback;             /*    96     8 */

        /* size: 104, cachelines: 2, members: 7 */
        /* sum members: 90, holes: 2, sum holes: 14 */
        /* last cacheline: 40 bytes */
};

After:

struct source_impl {
        struct spa_source          source;               /*     0    48 */
        struct impl *              impl;                 /*    48     8 */
        struct spa_list            link;                 /*    56    16 */
        /* --- cacheline 1 boundary (64 bytes) was 8 bytes ago --- */
        union {
                spa_source_io_func_t io;                 /*    72     8 */
                spa_source_idle_func_t idle;             /*    72     8 */
                spa_source_event_func_t event;           /*    72     8 */
                spa_source_timer_func_t timer;           /*    72     8 */
                spa_source_signal_func_t signal;         /*    72     8 */
        } func;                                          /*    72     8 */
        struct spa_source *        fallback;             /*    80     8 */
        _Bool                      close;                /*    88     1 */
        _Bool                      enabled;              /*    89     1 */

        /* size: 96, cachelines: 2, members: 7 */
        /* padding: 6 */
        /* last cacheline: 32 bytes */
};
2022-03-06 18:40:43 +00:00
Barnabás Pőcze
16f63a3c8f Revert "loop: remove destroy list"
This reverts commit c474846c42.
In addition, `s->loop` is also checked before dispatching a source.

The destroy list is needed in the presence of threads. The
issue is that a source may be destroyed between `epoll_wait()`
returning and thread loop lock being acquired. If this
source is active, then a use-after-free will be triggered
when the thread loop acquires the lock and starts dispatching
the sources.

  thread 1                       thread 2
 ----------                     ----------
                                loop_iterate
                                  spa_loop_control_hook_before
                                    // release lock

 pw_thread_loop_lock

                                  spa_system_pollfd_wait
                                    // assume it returns with source A

 pw_loop_destroy_source(..., A)
  // frees storage of A

 pw_thread_loop_unlock
                                  spa_loop_control_hook_after
                                    // acquire the lock

                                  for (...) {
                                    struct spa_source *s = ep[i].data;
                                    s->rmask = ep[i].events;
                                      // use-after-free if `s` refers to
                                      // the previously freed `A`

Fixes #2147
2022-02-18 20:31:14 +01:00
Barnabás Pőcze
bf886ba209 support: also protect against recursive invocations
Add an extra private field to the source to store the pollevent of
the current iteration. This changes ABI but it seems an embedded source
is not used outside of our own plugins and the unit test doesn't test
this ABI case.

Whenever a source is removed, we can set the data field of the
pollevent to NULL so that it won't be handled in any iteration anymore.

Avoid dispatching the same event multiple times when doing recursive
iterations.

Add some more unit tests for this.

Fixes #2114
2022-02-08 17:21:10 +01:00
Alexandre BIQUE
0b637c3291 support: loop_enter/leave hardening
This commit adds a counter for loop_enter/leave and checks:

 - consecutive enter are used on the same thread
 - leave is used on the same thread as enter
 - at destruction, the enter_count must be 0
2022-02-08 12:02:50 +01:00
Wim Taymans
c474846c42 loop: remove destroy list
Now that sources can't be dispatched anymore after a _remove, we don't
need to keep the destroy_list anymore and we can free the source
immediately.

See #2114
2022-02-08 10:18:02 +01:00
Wim Taymans
45d911641b loop: handle remove while dispatching better
Keep the array of dispatched sources around in the loop. When a source
is removed while dispatching, set the data to NULL so that we don't try
to deref the source again or call its function.

Fixes #2114
2022-02-08 10:17:13 +01:00
Wim Taymans
6ece5d810c loop: invoke immediately when loop is not running
Or else we might never get our callback called or worse, block forever,
waiting for the response.
2022-01-18 20:03:01 +01:00
Wim Taymans
30982775d9 support: use defines for alignment 2022-01-03 11:14:15 +01:00
Peter Hutterer
b3646743c1 spa: sprinkle more log topics into spa 2021-09-28 09:35:39 +02:00
Wim Taymans
78f52a7073 loop: avoid corruption of ringbuffer
The ringbuffer can't be written to from multiple threads.

When both the main loop and data thread do _invoke, they both write to
the ringbuffer and cause it to be corrupted because the ringbuffer is
not multi-writer safe.

Doing invoke from the thread itself is usually done to flush things out
so we really only need to flush the ringbuffer and call the callback.

See #1451
2021-07-26 11:39:48 +02:00
Wim Taymans
a91502b3e0 loop: improve invoke avail check
First calculate the size of the aligned payload and then check if
we can fit this aligned payload in the remaining space in the
ringbuffer.

Otherwise we might be able to fit the item + payload in the remaining
space but then place the alignment bytes at the begginning, which would
break alignment of the next invoke_item struct.
2021-07-19 10:12:15 +02:00
Wim Taymans
7f4fa64291 loop: Fix crash because of overflow
Also check if there is enough space to write the payload bytes.

We check if there is enough space for the invoke_item structure first.
Then we calculate how much bytes we need to use for the payload but we
fail to check if we can actually write that much data, risking
overwriting existing data from the ringbuffer and causing a crash later
when we try to jump to invalid memory.

Add some more comments.
2021-07-19 09:53:23 +02:00
Peter Hutterer
7697ed0757 treewide: replace strcmp() == 0 with spa_streq()
This change is only done in source files for now, header files will be done
separately.
2021-05-18 22:10:27 +10:00
Peter Hutterer
2405f0942b spa/buffer: rename SPA_MEMBER to SPA_PTROFF
SPA_MEMBER is misleading, all we're doing here is pointer+offset and a
type-casting the result. Rename to SPA_PTROFF which is more expressive (and
has the same number of characters so we don't need to re-indent).
2021-05-06 09:39:39 +00:00
Wim Taymans
f26c642055 loop: initialize some variables
Just in case the read fails.
2021-03-27 19:23:34 +01:00
Wim Taymans
4f816c1fb0 loop: never try to block in the thread
When we are calling invoke from the thread, the call will be completed
in the thread and there is no need to block for completion.
2021-03-10 13:01:19 +01:00
Wim Taymans
08ba6097a1 loop: align buffer and invoke_items to 8 bytes
To avoid unaligned access messages from sanitizer

See #497
2020-12-21 20:46:13 +01:00
Wim Taymans
0d9cc9e36e loop: always place the invoke item in the queue
Always place the invoke item in the queue and then either signal the
other thread or flush the queue when not already flushing.
2020-11-16 15:16:20 +01:00
Wim Taymans
b8b2ce6ba9 loop: always wake up blocking items
We should always wake up the blocking items if we scheduled the
item.
2020-11-13 16:50:23 +01:00
Wim Taymans
664ecbefee loop: move debug to _fp 2020-09-30 12:01:23 +02:00
Wim Taymans
a9c0435317 loop: don't recursively flush
If we are already in the loop thread and flushing, this means we
added a new invoke item on the list from a callback. Place the
item on the queue and let the flush code take care of it after the
callback completes.

Required to fix some issues with draining in pulse where a stream
is destroyed from the drained callback which then invokes a pause.
2020-09-17 11:46:55 +02:00
Wim Taymans
e5f7e040dc loop: simplify before and after events
Because the signal can't be removed from the callback we can
simply iterate backwards and then forwards.

The first added hook (the unlock/lock pair) is called last before
going into the poll and first when leaving. This executes all other
callbacks inside a locked situation. And removing them with the lock
is not going to cause problems.
2020-09-16 13:31:47 +02:00
Wim Taymans
05ae8a24de loop: make safe version of befor and after signal
Use a safer version of the before and after hooks. First call
all before hooks and save them in reverse order in a save list.
Then call the after event for the ones remaining in the save list
and move them back to the hook list.

This makes it possible to remove the hooks from one the callbacks or
even from other threads with the right locks. Found as a solution to
the following problem as observed in vlc:

main thread                            thread_loop

pw_thread_loop_lock()            before hook: lock suspend thread
pw_context_destroy()
  - removes before hook to flush clients
pw_thread_loop_unlock()
                                 before hook: lock acquired, resume
				 before hook: flush client hook executed
				   *crash*

pw_thread_loop_stop()
pw_thread_loop_destroy()

Any of the safer cursor methods (like spa_hook_list_call()) would also
work but are more expensive and don't reverse the before/after
order.
2020-09-15 20:13:32 +02:00
Wim Taymans
d1beeeade0 loop: handle file fd with eventfd
epoll return EPERM for file fds like stdin/stdout because they are
always ready for IO. use an idle source to handle these cases.
2020-04-14 18:05:45 +02:00
Wim Taymans
cb7bfdf98a sprinkly SPA_LIKELY/UNLIKELY around 2020-03-16 12:52:28 +01:00
Wim Taymans
f391353c7f Make interface types a string
This is more in line with wayland and it allows us to create new
interfaces in modules without having to add anything to the type
enum. It also removes some lookups to map type_id to readable
name in debug.
2019-12-19 13:36:04 +01:00
Wim Taymans
68e94a2e7e system: use spa_system functions for fds 2019-11-19 13:41:40 +01:00
Wim Taymans
47f0f1f9a3 loop: flush items in invoke from thread
When we do invoke from the loop thread, first process the pending
invoke items before handling ours. This can be used to sync the
invoke queue before cleanup.
2019-08-16 15:04:27 +02:00
Wim Taymans
37b57caf11 loop: pass loop as first argument in function 2019-08-15 18:38:32 +02:00
Wim Taymans
6720ded529 names: add standard factory name definitions
Define a set of standard factory names and document what they
contain. This makes it possible to change the implementation by
mapping the factory-name to a different shared library.
2019-06-21 13:31:34 +02:00
Wim Taymans
5b7e95c71c system: make system functions return error on error
Return -errno from system functions instead of -1 in errors. This
makes it easier to pass along the result without having to go to
errno etc..
2019-06-20 17:31:29 +02:00
Wim Taymans
d1241e2c1c improve error handling
Make sure we free properties we take ownership of.
Add some more return values to functions that can fail.
2019-06-19 18:15:04 +02:00
Wim Taymans
5850044599 improve error handling 2019-06-18 16:55:37 +02:00
Wim Taymans
504d78cd18 improve error handling
Set errno for functions returning NULL if relevant.
Propagate errno and result codes better.
Handle more error cases.
2019-06-07 10:11:23 +02:00
Wim Taymans
db88e9f954 System: More work on making system functions pluggable
Move the epoll functions to the system functions and make the loop
use those. Use simple mask for events instead of enum.
Add the used system api in pw_loop.
Add System API to spa_support and use it where possible.
Pass the system API used in the realtime loops in spa_support as
well and use this in the realtime paths.
Improve bootstrapping, load only the log and cpu interfaces because
those can/need to be shared between instances. Let the core load
the other interfaces.
Add keys to configure the System and Loop implementations used in
pw_loop.
2019-06-06 15:31:53 +02:00
Wim Taymans
81c7dd4433 support: abstract some system functions
Make a new API to hide some the implementation of eventfd, timerfd
and signalfd along with clock and read/write/ioctl/close functions.
We would like to have plugins use the abstractions so that we
can switch them to something else when needed.
2019-06-04 17:07:34 +02:00
Wim Taymans
7bb6515800 loop: cleanups 2019-05-23 15:11:49 +02:00
Wim Taymans
ff946e3d4b interface: add an interface struct
The interface struct has the type,version and methods of the
interface.
Make spa interfaces extend from spa_interface and make a
separate structure for the methods.
Pass a generic void* as the first argument of methods, like
we don in PipeWire.
Bundle the methods + implementation in a versioned inteface
and use that to invoke methods. This way we can do version
checks on the methods.
Make resource and proxy interfaces that we can can call. We
can then make the core interfaces independent on proxy/resource and
hide them in the lower layers.
Add add_listener method to methods of core interfaces, just
like SPA.
2019-05-23 12:59:24 +02:00
Wim Taymans
7b12212eeb node: improve async handling
Remove the done and error callbacks. The error callback is in an
error message. The done callback is replace with spa_pending.

Make enum_params take a callback and data for the results. This allows
us to push the results one after another to the app and avoids ownership
issues of the passed data. We can then extend this to handle the async
case by doing a _wait call with a spa_pending+callback+data that will
be called when the _enum_params returns and async result.
Add a sync method.

All methods can now return SPA_RESULT_IS_ASYNC return values and you
can use spa_node_wait() to register a callback when they complete
with optional extra parameters. This makes it easier to sync and
handle the reply.

Make helper methods to simulate the sync enum_params behaviour for
sync nodes.

Let the transport generate the sequence number for pw_resource_sync()
and pw_proxy_sync(). That way we don't need to keep track of numbers
ourselves and we can match the reply to the request easily.
2019-02-21 09:40:12 +01:00
Wim Taymans
a563050797 spa: don't use constructor attribute to register factories 2019-02-06 12:14:25 +01:00
Wim Taymans
e918f9f77c fix sign confusion 2019-01-07 15:52:42 +01:00
Wim Taymans
85f2e93c54 Relicense as MIT/X11
Remove some obsolete files
2018-11-05 17:48:52 +01:00
Wim Taymans
39240fb9c0 use _list_consume to destroy objects 2018-09-25 13:05:13 +02:00
Wim Taymans
90c1a95eef More type cleanups
SPA_ID_* -> SPA_TYPE_*. We use 'type' for all registered types.
ID refers to either a registered type or an enum.
Improve introspection of object ids. Make 0 an invalid enum and
use it to look up the object id type.
Move some type-info files together.
Swap type and id of the object, we first specify the type and then
the id because the meaning of the id depends on the object type.
2018-08-27 15:03:11 +02:00
Wim Taymans
fca3e1d85d Remove dynamic types
Do not use dynamic types anymore. The reason is that it's difficult:

- to maintain a shared type database over a network.
- the extra overhead when translating between processes and for
  maintaining the translation tables.
- race conditions in translating in RT-threads, this is a problem
  because we want to make event streams.

We now have simple enums with types and extension points for all
types. This is also nicer to use in general.
We don't need the mapper anymore or pass strings around as types.
There is a parallel type info system to get more info about ids and
enums and their hierarchy. It can also be used for debugging.
2018-08-23 17:47:57 +02:00
Wim Taymans
a63523650d hooks: enforce version on hook emission
Pass the minimum required version to the hook emission and only call
events when the handler is recent enough.
Add some macros to make event emission easier to read.
2018-08-01 21:41:25 +02:00
Wim Taymans
1bfb1d9228 loop: return number of dispatched fds and update docs 2018-06-01 11:16:53 +02:00