How libavif Ensures Thread Safety During Decoding
This article explores how libavif, the library used for
encoding and decoding AVIF images, manages thread safety during
concurrent decoding operations. We will examine its stateless design,
instance isolation, reliance on reentrant code, and how it safely
delegates multi-threaded processing to underlying AV1 codecs like
dav1d and libaom to prevent race
conditions.
Isolated Decoder
Instances (avifDecoder)
The primary way libavif ensures thread safety is through
its object-oriented architecture, which relies on isolated instances of
the avifDecoder structure.
- No Shared Decoder State: Each thread performing a
decoding operation must create and use its own
avifDecoderinstance. Because state information, parsing progress, and image buffers are contained entirely within individual decoder objects, threads do not interfere with each other. - Concurrent Decodes: You can safely decode multiple
AVIF files concurrently on different threads, provided each thread
operates on its own distinct
avifDecoderinstance.
Immutable Input Data
During concurrent decoding, multiple threads may need to read from
the same source data (such as a shared memory buffer containing the raw
AVIF file). libavif handles this safely by treating input
data as read-only.
- Read-Only Buffers: The input buffer containing the
AVIF container data is passed to
avifDecoderSetIOMemory()or a custom reader. Because the decoding process only reads from this buffer and never writes to it, multiple threads can safely read from the same underlying memory address simultaneously without synchronization locks.
Reentrant Functions and No Global State
libavif is written in C and designed to be fully
reentrant.
- No Global Variables: The library avoids the use of global or static variables for storing state during the decoding process. All variables are stored on the stack or allocated dynamically within the scope of the specific decoder instance.
- Independent Function Execution: Functions like
avifDecoderParse()andavifDecoderNextImage()only operate on the data structures passed to them as arguments. This guarantees that calling these functions concurrently on different threads will not result in data corruption.
Delegation to Thread-Safe AV1 Codecs
libavif is a container parser; the actual decompression
of the AV1 video bitstream inside the AVIF container is delegated to
external codecs such as dav1d or libaom
(AOMedia Video 1).
- Codec Isolation: When
libavifinitializes an underlying codec for a specificavifDecoderinstance, it creates a private codec context. This context is not shared with otherlibavifdecoder instances. - Internal Thread Pools: Modern AV1 decoders like
dav1dare highly optimized and feature their own internal thread pools for frame-level and tile-level decoding.libavifconfigures these libraries by passing down thread-count limits, ensuring that the codec’s internal multi-threading is safely isolated within the thread that calledlibavif.
Thread-Safe Memory Allocation
By default, libavif uses standard system memory
allocation functions (malloc, free), which are
thread-safe on all modern operating systems. If you choose to configure
a custom memory allocator using avifsetAllocator(), you
must ensure that your custom allocator is thread-safe, as
libavif assumes the underlying memory manager can handle
concurrent requests safely.