MediaLibrary: Add priority access mechanism
In addition to read-lock and write-lock, add a "priority access" mechanism. The goal is to give priority to UI requests.
Several callers may acquire a "priority access". As soon as at least one caller has priority access, all non-priority callers are blocked on lock_read()
and lock_write()
, until all priority accesses are released. However, they can continue executing their current requests following the traditional RW-lock rules.
Acquiring a priority access is "immediate". It is different from a lock in that it never blocks but makes other clients wait.
Context
When the VLC UI loads a list of items, it generates many read requests to the medialibrary, each one protected by a read-lock, but not globally protected by a single lock/unlock. As a consequence, during indexation, many write requests were executed between these read requests, causing the loading time to explode (several tens of seconds instead of milliseconds).
Alternative considered: exclusive lock
To avoid interleaving of read requests from the UI and write requests from the indexation, I first wrote a PoC to expose an exclusive lock in the medialib API. The UI took the exclusive lock for executing all its read requests within a single lock. This solved the performance issue.
However, an exclusive lock for solving this problem was not very suitable in theory:
- if there was already read-requests running, taking an exclusive lock delayed the request (while the goal is to make it faster), often for no reason because requests for the UI are typically read-requests
- an exclusive lock prevents several read-requests in parallel (useful for updating several parts of the UI loaded by background threads)
- the implementation actually exposed the internal write lock, which had to be made recursive (and only the write-lock of the RW-lock was made recursive, the read-lock was not because it's more difficult/inefficient, which is a bit odd)
- semantically, taking a write lock (exclusive lock) for read-requests, because its side-effects (prevent unwanted request interleaving) improve performance, sounds a bit hacky.
Priority access
To avoid these drawbacks, this MR implements a "priority access" instead. It is different from a lock in that it never blocks the caller but makes other (non-priority) clients wait. In bulk:
- a client can acquire a priority access the same way it acquires locks (
auto _ = ml->acquirePriorityAccess()
) - acquiring a priority access is always "immediate" (the caller will never "wait")
- any number of clients can acquire a priority access simultaneously
- when there is at least one client with priority access, all non-priority clients will block on
lock_read()
orlock_write()
until all priority accesses are released - if other non-priority clients already acquired a read-lock or a write-lock when a client acquire a priority access, they can continue to execute their requests, following the traditional RW-lock rules
- the priority clients may still be blocked on
lock_read()
andlock_write()
by others (priority or non-priority) clients due to traditional RW-lock rules
In practice, indexation requests don't have priority access, so they are all blocked when a UI request, which will take the priority access, is started.
Like with the "exclusive lock" PoC, the list results is retrieved very quickly even when indexing is running.