# Thread Safety Guarantees for ImageBuf During Concurrent Read/Write Operations

> Understand ImageBuf thread safety. Learn about concurrent read guarantees, potential race conditions with writes, and exclusive access needs for ImageBuf operations.

- Repository: [Academy Software Foundation/openimageio](https://github.com/academysoftwarefoundation/openimageio)
- Tags: thread-safety
- Published: 2026-02-23

---

**ImageBuf supports thread-safe concurrent reads from multiple threads, permits concurrent writes that will not crash but may produce race conditions on overlapping pixels, and requires exclusive access for construction, destruction, reset operations, and metadata modifications.**

The `ImageBuf` class in the Academy Software Foundation's OpenImageIO (OIIO) library serves as the primary in-memory container for image data. Understanding the thread safety guarantees for `ImageBuf` during concurrent read/write operations is essential for building high-performance, multi-threaded image processing pipelines that maximize throughput without risking data corruption or undefined behavior.

## Read-Only Thread Safety Guarantees

All **read-only methods** in `ImageBuf` are fully thread-safe and may be called concurrently from any number of threads without additional synchronization.

### Safe Concurrent Read Methods

The following operations are guaranteed thread-safe according to the source documentation in [`src/include/OpenImageIO/imagebuf.h`](https://github.com/academysoftwarefoundation/openimageio/blob/main/src/include/OpenImageIO/imagebuf.h) (lines 106-112):

- `init_spec()`
- `read()`
- `spec()`
- Any `getpixel*` flavor (e.g., `getpixel`, `get_pixels`)
- `ConstIterator`
- `roi()` and related geometry queries

These functions only access immutable internal state or use atomic operations where necessary, ensuring that multiple threads can safely read pixel data, metadata, or image specifications simultaneously.

## Write Operation Thread Safety

**Pixel-modifying methods** provide a weaker guarantee: they are thread-safe in the sense that concurrent calls will not cause memory corruption or crashes, but they are susceptible to race conditions when accessing the same pixels.

### Pixel-Modifying Methods and Race Conditions

The implementation protects internal data structures so that methods such as `setpixel*`, non-const `Iterator`, and `write()` will not crash when called concurrently. However, as documented in [`src/include/OpenImageIO/imagebuf.h`](https://github.com/academysoftwarefoundation/openimageio/blob/main/src/include/OpenImageIO/imagebuf.h) (lines 113-119), if two threads write to the same pixel simultaneously, or one thread writes while another reads the same pixel, the final image state may be inconsistent or contain corrupted pixel values.

This "crash-safe but data-race possible" semantics allows for high-performance parallel processing when threads operate on disjoint pixel regions, but requires explicit synchronization for overlapping access patterns.

## Non-Thread-Safe Operations

Certain lifecycle and metadata operations are **explicitly not thread-safe** and require exclusive access to the `ImageBuf` instance.

### Construction, Destruction, and Metadata Changes

As stated in [`src/include/OpenImageIO/imagebuf.h`](https://github.com/academysoftwarefoundation/openimageio/blob/main/src/include/OpenImageIO/imagebuf.h) (lines 120-124), the following operations must never be performed concurrently on the same `ImageBuf` instance:

- Construction and destruction
- `reset()`
- `specmod()` and any method that alters image metadata

Performing these operations concurrently can corrupt the object's internal state, leading to undefined behavior or crashes. Always ensure these lifecycle events are serialized, either by restricting them to a single thread or by using external synchronization primitives.

## Practical Implementation Strategies

### Concurrent Reading Patterns

For read-heavy workloads, share a const reference or copy of the `ImageBuf` handle across threads. The following example demonstrates safe concurrent scanline reading:

```cpp
#include <OpenImageIO/imagebuf.h>
#include <thread>
#include <vector>

using namespace OIIO;

void thread_reader(const ImageBuf& img, int y) {
    // Each thread reads a whole scanline (read-only)
    std::vector<float> line(img.spec().width * img.nchannels());
    img.getpixel(0, y, line.data());   // thread-safe
}

// Launch N threads, each reading a different row
std::vector<std::thread> readers;
for (int y = 0; y < img.spec().height; ++y)
    readers.emplace_back(thread_reader, std::cref(img), y);
for (auto& t : readers) t.join();

```

### Safe Concurrent Writing with Spatial Partitioning

To avoid race conditions during writes, partition the image so each thread works on a distinct region. The following example uses quadrant-based decomposition:

```cpp
void thread_writer(ImageBuf& img, int x0, int y0, int w, int h) {
    // Fill a tile with a constant value
    for (int y = y0; y < y0 + h; ++y)
        for (int x = x0; x < x0 + w; ++x)
            img.setpixel(x, y, {1.0f, 0.0f, 0.0f}); // no overlap → safe
}

// Divide the image into 4 quadrants
int w = img.spec().width / 2, h = img.spec().height / 2;
std::thread t1(thread_writer, std::ref(img), 0,   0,   w, h);
std::thread t2(thread_writer, std::ref(img), w,   0,   w, h);
std::thread t3(thread_writer, std::ref(img), 0,   h,   w, h);
std::thread t4(thread_writer, std::ref(img), w,   h,   w, h);
t1.join(); t2.join(); t3.join(); t4.join();

```

### Protecting Overlapping Writes with Synchronization

When threads must write to overlapping regions, use explicit synchronization. The OpenImageIO library provides spin-lock and mutex utilities in [`thread.h`](https://github.com/academysoftwarefoundation/openimageio/blob/main/thread.h), or you can use standard C++ primitives:

```cpp
#include <mutex>

std::mutex img_mutex;

void thread_overlap_writer(ImageBuf& img, int x0, int y0, int w, int h) {
    std::lock_guard<std::mutex> lock(img_mutex); // serialize overlapping region
    for (int y = y0; y < y0 + h; ++y)
        for (int x = x0; x < x0 + w; ++x)
            img.setpixel(x, y, {0.0f, 1.0f, 0.0f});
}

```

For higher performance in read-heavy scenarios with occasional writes, consider the spin-read-write mutex utilities found in [`src/include/OpenImageIO/thread.h`](https://github.com/academysoftwarefoundation/openimageio/blob/main/src/include/OpenImageIO/thread.h) (lines 401-433).

## Key Source Files and Implementation Details

The thread safety guarantees are explicitly documented and implemented in the following source files:

| File | Relevance |
|------|-----------|
| [`src/include/OpenImageIO/imagebuf.h`](https://github.com/academysoftwarefoundation/openimageio/blob/main/src/include/OpenImageIO/imagebuf.h) | Contains the authoritative thread-safety contract for `ImageBuf` (lines 106-124), categorizing methods as thread-safe read-only, crash-safe writes, and non-thread-safe lifecycle operations. |
| [`src/include/OpenImageIO/thread.h`](https://github.com/academysoftwarefoundation/openimageio/blob/main/src/include/OpenImageIO/thread.h) | Provides low-level synchronization primitives including spin-lock mutexes and mutex-pool utilities (lines 401-433) for implementing custom synchronization strategies. |
| [`src/libOpenImageIO/imagebuf.cpp`](https://github.com/academysoftwarefoundation/openimageio/blob/main/src/libOpenImageIO/imagebuf.cpp) | Implements the `ImageBuf` methods referenced above, demonstrating internal use of atomic counters and locking mechanisms where applicable. |
| [`src/include/OpenImageIO/imageio.h`](https://github.com/academysoftwarefoundation/openimageio/blob/main/src/include/OpenImageIO/imageio.h) | Documents optional per-image-IO thread-safety hooks that plugin implementations can leverage (lines 2168-2172). |

## Summary

- **Concurrent reads** are fully thread-safe; multiple threads may call `getpixel()`, `spec()`, `ConstIterator`, and other read-only methods simultaneously without synchronization.
- **Concurrent writes** will not crash or corrupt memory, but race conditions occur if threads access the same pixels; partition work into disjoint regions or use explicit locks.
- **Lifecycle operations** including construction, destruction, `reset()`, and metadata modifications via `specmod()` are not thread-safe and require exclusive access.
- **Synchronization utilities** in [`thread.h`](https://github.com/academysoftwarefoundation/openimageio/blob/main/thread.h) provide high-performance mutexes for custom locking strategies when overlapping write access is unavoidable.

## Frequently Asked Questions

### Can multiple threads read from the same ImageBuf simultaneously?

Yes. According to the OpenImageIO source code in [`src/include/OpenImageIO/imagebuf.h`](https://github.com/academysoftwarefoundation/openimageio/blob/main/src/include/OpenImageIO/imagebuf.h) (lines 106-112), all read-only methods—including `getpixel()`, `spec()`, `roi()`, and `ConstIterator`—are thread-safe and may be called concurrently from any number of threads without additional synchronization.

### Is it safe to call setpixel() from multiple threads on different pixels?

Yes, provided the threads access disjoint pixel regions. The `setpixel()` method and non-const `Iterator` operations are thread-safe in the sense that they will not cause memory corruption or crashes. However, as documented in [`src/include/OpenImageIO/imagebuf.h`](https://github.com/academysoftwarefoundation/openimageio/blob/main/src/include/OpenImageIO/imagebuf.h) (lines 113-119), if threads write to different pixels, the operation is safe; if they write to the same pixel, race conditions will result in undefined final values.

### What happens if two threads write to the same pixel simultaneously?

The final pixel value becomes nondeterministic. While the internal data structures are protected against crashes, the actual pixel data is not locked during write operations. As stated in the source documentation, concurrent writes to the same pixel (or read-while-write scenarios) may produce an inconsistent resulting image because the operations are not atomic at the pixel level.

### Do I need to lock ImageBuf during construction or reset()?

Yes. Construction, destruction, `reset()`, and metadata-modifying methods such as `specmod()` are explicitly not thread-safe according to [`src/include/OpenImageIO/imagebuf.h`](https://github.com/academysoftwarefoundation/openimageio/blob/main/src/include/OpenImageIO/imagebuf.h) (lines 120-124). You must ensure these operations are serialized, either by restricting them to a single thread or by using external synchronization primitives such as `std::mutex` or the spin-lock utilities provided in [`src/include/OpenImageIO/thread.h`](https://github.com/academysoftwarefoundation/openimageio/blob/main/src/include/OpenImageIO/thread.h).