# Weak Reference Management for OBS Objects: Complete Guide to obs_weak_* Types

> Master weak reference management for OBS objects using obs_weak_* types. Learn thread-safe atomic reference counting to prevent use-after-free errors and manage handles effectively.

- Repository: [OBS Project/obs-studio](https://github.com/obsproject/obs-studio)
- Tags: internals
- Published: 2026-03-03

---

**OBS Studio implements thread-safe weak reference management for objects with `obs_weak_*` types using atomic reference counting, allowing non-owning handles to sources, outputs, and encoders without preventing garbage collection or risking use-after-free errors.**

Weak reference management for objects with `obs_weak_*` types forms the backbone of OBS Studio's memory safety architecture, enabling plugins and core modules to track object lifetimes without retaining ownership. The obsproject/obs-studio repository implements this pattern through the libobs subsystem, providing atomic reference counting for core objects including sources, outputs, encoders, services, and canvases.

## Why Weak References Matter in OBS Studio

OBS Studio operates across multiple threads—audio callbacks, video rendering, and UI events all access shared objects concurrently. Strong references (traditional ownership) would force every subsystem to coordinate destruction, creating complex circular dependencies. Weak references solve this by allowing code to hold a handle that can detect when an object dies without preventing that destruction.

The `obs_weak_*` system provides three critical guarantees:

- **Non-blocking expiration detection**: Check if an object was destroyed without acquiring locks.
- **Safe promotion**: Atomically upgrade a weak handle to a strong reference if the object still exists.
- **Automatic cleanup**: Weak control blocks free themselves when all references (strong and weak) disappear.

## Core Architecture of obs_weak_* Types

### The obs_weak_ref Structure

All weak types embed a common atomic control block defined in [`libobs/obs-internal.h`](https://github.com/obsproject/obs-studio/blob/main/libobs/obs-internal.h):

```c
struct obs_weak_ref {
    volatile long refs;       // strong reference count
    volatile long weak_refs;  // weak reference count
};

```

This structure lives in the object's control block, allocated at creation time. The `volatile` qualifier ensures compiler optimizations do not reorder atomic operations across threads.

### Specialized Weak Types

OBS defines type-specific weak structures for each core object, all following the same pattern:

| Weak Type | Underlying Object | Definition Location |
|-----------|-------------------|---------------------|
| `obs_weak_object_t` | `obs_object_t` | [`libobs/obs-internal.h`](https://github.com/obsproject/obs-studio/blob/main/libobs/obs-internal.h) |
| `obs_weak_source_t` | `obs_source_t` | [`libobs/obs-internal.h`](https://github.com/obsproject/obs-studio/blob/main/libobs/obs-internal.h) |
| `obs_weak_output_t` | `obs_output_t` | [`libobs/obs-internal.h`](https://github.com/obsproject/obs-studio/blob/main/libobs/obs-internal.h) |
| `obs_weak_encoder_t` | `obs_encoder_t` | [`libobs/obs-internal.h`](https://github.com/obsproject/obs-studio/blob/main/libobs/obs-internal.h) |
| `obs_weak_service_t` | `obs_service_t` | [`libobs/obs-internal.h`](https://github.com/obsproject/obs-studio/blob/main/libobs/obs-internal.h) |
| `obs_weak_canvas_t` | `obs_canvas_t` | [`libobs/obs-internal.h`](https://github.com/obsproject/obs-studio/blob/main/libobs/obs-internal.h) |

Each structure contains exactly two members: an `obs_weak_ref` and a pointer to the underlying object. For example, the source variant:

```c
struct obs_weak_source {
    struct obs_weak_ref ref;
    struct obs_source *source;
};

```

## Internal Reference Counting API

The atomic operations governing weak references reside in [`libobs/obs-internal.h`](https://github.com/obsproject/obs-studio/blob/main/libobs/obs-internal.h) (lines 75-111). These functions operate directly on `obs_weak_ref` structures:

- **`obs_ref_addref`** / **`obs_ref_release`**: Manipulate the strong reference count (`refs`). When `refs` reaches `-1`, the object enters a destroyed state.
- **`obs_weak_ref_addref`** / **`obs_weak_ref_release`**: Manipulate the weak reference count (`weak_refs`). When both counts reach their terminal values, the control block frees itself via `bfree`.
- **`obs_weak_ref_get_ref`**: Attempts to increment the strong count if the object is still alive (atomic compare-and-swap operation). Returns `true` if successful, `false` if the object was already destroyed.
- **`obs_weak_ref_expired`**: Returns `true` if `refs < 0`, indicating the underlying object has been destroyed.

## Public API for Weak Reference Management

The header [`libobs/obs.h`](https://github.com/obsproject/obs-studio/blob/main/libobs/obs.h) exposes type-safe wrappers for each object category. These functions hide the internal `obs_weak_ref` mechanics while providing the same atomic guarantees:

```c
/* Generic object weak references */
EXPORT void obs_weak_object_addref(obs_weak_object_t *weak);
EXPORT void obs_weak_object_release(obs_weak_object_t *weak);
EXPORT obs_weak_object_t *obs_object_get_weak_object(obs_object_t *object);
EXPORT obs_object_t *obs_weak_object_get_object(obs_weak_object_t *weak);
EXPORT bool obs_weak_object_expired(obs_weak_object_t *weak);
EXPORT bool obs_weak_object_references_object(obs_weak_object_t *weak,
                                             obs_object_t *object);

/* Source-specific variants */
EXPORT obs_weak_source_t *obs_source_get_weak_source(obs_source_t *source);
EXPORT obs_source_t *obs_weak_source_get_source(obs_weak_source_t *weak);
EXPORT void obs_weak_source_release(obs_weak_source_t *weak);
/* ... and similar for output, encoder, service, canvas */

```

## Lifecycle Operations

### Creating Weak References

When an object is created, its `obs_context_data` structure receives a pointer to a newly allocated weak control block. The macro `get_weak` retrieves this control block from the context:

```c
#define get_weak(source) ((obs_weak_source_t *)source->context.control)

```

To obtain a weak handle from a strong pointer:

```c
obs_weak_source_t *weak = obs_source_get_weak_source(source);

```

This function, defined in [`libobs/obs-source.c`](https://github.com/obsproject/obs-studio/blob/main/libobs/obs-source.c), increments the weak reference count before returning the handle.

### Promoting to Strong References

The critical safety feature is the ability to atomically upgrade a weak reference to a strong one:

```c
obs_source_t *src = obs_weak_source_get_source(weak);

```

Internally, this calls `obs_weak_ref_get_ref` in [`libobs/obs-internal.h`](https://github.com/obsproject/obs-studio/blob/main/libobs/obs-internal.h). If the strong count is still valid (not `-1`), the function atomically increments it and returns the underlying pointer. If the object was destroyed between the check and the increment, it returns `NULL`.

### Detecting Expiration

Before attempting promotion, you can check if the object still exists:

```c
if (obs_weak_source_expired(weak)) {
    /* Object is already destroyed */
}

```

This checks if `refs < 0` in the control block, indicating the destructor has run.

### Cleanup and Release

When finished with a weak handle, release it to prevent memory leaks:

```c
obs_weak_source_release(weak);

```

This decrements the weak reference count. When both the strong and weak counts reach their terminal values, the control block automatically frees itself via `bfree`.

## Practical Implementation Patterns

### Pattern 1: Callback Safety with Weak Sources

When registering callbacks that may fire after an object is destroyed, store a weak reference rather than a strong one:

```c
/* Store weak handle during registration */
obs_weak_source_t *weak_src = obs_source_get_weak_source(my_source);

void my_callback(void *data, void *param)
{
    obs_weak_source_t *weak = (obs_weak_source_t *)param;
    
    /* Attempt promotion; returns NULL if destroyed */
    obs_source_t *src = obs_weak_source_get_source(weak);
    if (!src)
        return;  /* Source was destroyed between callback queue and execution */
    
    /* Safe to use src here */
    process_source_data(src);
    
    /* Release the temporary strong reference */
    obs_source_release(src);
}

/* Cleanup when unregistering */
obs_weak_source_release(weak_src);

```

This pattern appears throughout [`libobs/obs-source.c`](https://github.com/obsproject/obs-studio/blob/main/libobs/obs-source.c) when handling source removal callbacks.

### Pattern 2: Validity Checking for Outputs

UI code often needs to check if an output is still valid before updating status indicators:

```c
obs_weak_output_t *weak_out = obs_output_get_weak_output(my_output);

/* Later, perhaps from a UI timer */
if (!obs_weak_output_expired(weak_out)) {
    obs_output_t *out = obs_weak_output_get_output(weak_out);
    
    /* Safe to query output status */
    bool active = obs_output_active(out);
    update_ui_status(active);
    
    obs_output_release(out);
} else {
    update_ui_status(false);  /* Output destroyed */
}

obs_weak_output_release(weak_out);

```

This avoids crashes when outputs are destroyed while UI callbacks are pending.

## Key Source Files and Implementation Details

| File | Role | Key Components |
|------|------|----------------|
| [`libobs/obs-internal.h`](https://github.com/obsproject/obs-studio/blob/main/libobs/obs-internal.h) | Core weak reference structures | `struct obs_weak_ref`, `obs_ref_addref`, `obs_weak_ref_get_ref` |
| [`libobs/obs.h`](https://github.com/obsproject/obs-studio/blob/main/libobs/obs.h) | Public API declarations | `obs_weak_object_addref`, `obs_source_get_weak_source`, `obs_weak_output_get_output` |
| [`libobs/obs.c`](https://github.com/obsproject/obs-studio/blob/main/libobs/obs.c) | Generic object weak reference implementation | `obs_weak_object_get_object`, `obs_weak_object_expired` |
| [`libobs/obs-source.c`](https://github.com/obsproject/obs-studio/blob/main/libobs/obs-source.c) | Source-specific weak reference management | `obs_source_get_weak_source`, `get_weak` macro |
| [`libobs/obs-output.c`](https://github.com/obsproject/obs-studio/blob/main/libobs/obs-output.c) | Output weak reference implementation | `obs_output_get_weak_output`, `obs_weak_output_release` |
| [`libobs/obs-encoder.c`](https://github.com/obsproject/obs-studio/blob/main/libobs/obs-encoder.c) | Encoder weak reference handling | `obs_encoder_get_weak_encoder` |
| [`libobs/obs-service.c`](https://github.com/obsproject/obs-studio/blob/main/libobs/obs-service.c) | Service weak reference functions | `obs_service_get_weak_service` |
| [`libobs/obs-canvas.c`](https://github.com/obsproject/obs-studio/blob/main/libobs/obs-canvas.c) | Canvas weak reference management | `obs_canvas_get_weak_canvas` |

## Summary

- **Atomic safety**: The `obs_weak_ref` structure uses `volatile long` counters with atomic operations to ensure thread-safe reference counting across OBS's multi-threaded architecture.
- **Non-owning handles**: Weak references allow callbacks and UI code to track object lifetimes without preventing destruction, solving circular dependency issues in scene graphs.
- **Promotion pattern**: Always use `obs_weak_*_get_*` functions to attempt atomic promotion from weak to strong references, checking for `NULL` returns before dereferencing.
- **Expiration detection**: Use `obs_weak_*_expired` to check object status without creating temporary strong references, useful for UI polling scenarios.
- **Mandatory cleanup**: Always pair `obs_*_get_weak_*` calls with `obs_weak_*_release` to prevent memory leaks in the control blocks.

## Frequently Asked Questions

### How do I safely access an object after storing a weak reference?

Always attempt to promote the weak reference to a strong one using the appropriate `obs_weak_*_get_*` function. For example, call `obs_weak_source_get_source(weak)` and check if the return value is non-NULL. If successful, you receive a strong reference that prevents destruction while you use it. Remember to call `obs_source_release` (or the appropriate release function) when finished to decrement the strong count.

### What happens if I call obs_weak_source_get_source after the source is destroyed?

The function returns `NULL`. Internally, `obs_weak_ref_get_ref` checks the strong reference count in the control block. If the count indicates the object has been destroyed (values less than 0), the atomic compare-and-swap operation fails, and the promotion function returns `NULL` instead of a dangling pointer. This makes weak references safe to use even in asynchronous callbacks where the object might disappear between check and use.

### Can I check if a weak reference is valid without creating a strong reference?

Yes. Use the `obs_weak_*_expired` functions (such as `obs_weak_output_expired` or `obs_weak_source_expired`). These check if the strong reference count has dropped below zero, indicating the underlying object has been destroyed. This is useful for UI polling or logging where you want to know status without the overhead of temporarily incrementing the strong reference count. However, for actual object access, you must still use the promotion functions to obtain a strong reference.

### Where are the weak reference control blocks allocated and freed?

Control blocks are allocated when objects are created (e.g., during `obs_source_create` or `obs_output_create`) and stored in the `obs_context_data` structure's control pointer. The block persists until both strong and weak reference counts reach terminal values. When the last strong reference is released, the object destructor runs but the control block remains if weak references exist. Once the last weak reference is released via `obs_weak_*_release`, the control block is freed using `bfree` in [`libobs/obs-internal.h`](https://github.com/obsproject/obs-studio/blob/main/libobs/obs-internal.h).