# How to Configure Custom Exception Handling with MulleObjCSetupExceptionHandler in Mulle-ObjC

> Learn how to configure custom exception handling in Mulle-ObjC. Install a thread-local uncaught exception handler using MulleObjCSetupExceptionHandler for robust error management.

- Repository: [mulle-objc/mulleobjc](https://github.com/mulle-objc/mulleobjc)
- Tags: how-to-guide
- Published: 2026-03-07

---

**Call `MulleObjCSetupExceptionHandler` with a function pointer to install a thread-local uncaught exception handler that the Mulle-ObjC runtime invokes when `MulleObjCRaiseException` propagates uncaught.**

The Mulle-ObjC runtime provides a lightweight, thread-safe mechanism for handling uncaught exceptions through the `MulleObjCSetupExceptionHandler` function. Unlike traditional Objective-C runtimes that rely on global handlers, Mulle-ObjC stores exception handlers per-thread within the universal runtime structure, allowing fine-grained control over error recovery and logging strategies in the **mulle-objc/mulleobjc** repository.

## Understanding the Exception Handler API

### Core Function Signature

The primary interface is declared in [`src/function/MulleObjCExceptionHandler.h`](https://github.com/mulle-objc/mulleobjc/blob/main/src/function/MulleObjCExceptionHandler.h). The function accepts a single parameter: a pointer to a handler function that matches the signature `void handler(void)`.

```c
void MulleObjCSetupExceptionHandler(void (*handler)(void));

```

When you pass a non-NULL function pointer, the runtime stores that address in the current thread's universe structure (`struct _mulle_objc_universe`). Passing `NULL` effectively removes any previously installed handler for that thread.

### Thread-Local Storage Architecture

The handler is stored on a per-thread basis, not globally. This design ensures that different threads can maintain independent exception handling strategies without interfering with each other. Each thread must invoke `MulleObjCSetupExceptionHandler` independently if it requires custom handling; otherwise, uncaught exceptions in that thread will cause immediate process termination.

## Installing a Custom Exception Handler

### Basic Implementation Pattern

To configure handling, define a callback function and register it at thread startup. The handler should perform cleanup or logging, then terminate, because the runtime does not resume execution after the handler returns.

```c
#include "src/function/MulleObjCExceptionHandler.h"
#include <stdio.h>
#include <stdlib.h>

/* Custom handler invoked when an exception is not caught */
static void my_exception_handler(void)
{
    fprintf(stderr, "[ERROR] Uncaught exception detected. Performing cleanup...\n");
    /* Insert resource cleanup or crash reporting here */
    abort();  /* Terminate to prevent undefined behavior */
}

int main(void)
{
    /* Install handler for the main thread */
    MulleObjCSetupExceptionHandler(my_exception_handler);
    
    /* Application code proceeds... */
    return 0;
}

```

### NS-Style Wrapper Functions

For compatibility with conventional Objective-C patterns, the header also provides **NSSetUncaughtExceptionHandler** and **NSGetUncaughtExceptionHandler**, which act as thin wrappers around the core `MulleObjCSetupExceptionHandler` mechanism. These wrappers access the same underlying thread-local storage slot.

```c
/* Equivalent to MulleObjCSetupExceptionHandler(my_handler); */
NSSetUncaughtExceptionHandler(my_exception_handler);

/* Retrieve the currently installed handler */
void (*current_handler)(void) = NSGetUncaughtExceptionHandler();

```

## Raising Exceptions and Handler Invocation

### The Exception Propagation Flow

When code calls `MulleObjCRaiseException(id exception)`, the runtime unwinds the stack searching for a catch handler. If no catch block intercepts the exception, the runtime checks for an installed handler in the current thread's universe structure. If present, it invokes that handler; otherwise, it terminates the process immediately without additional logging.

### Practical Raising Example

The following example demonstrates raising an exception that triggers the custom handler installed via `MulleObjCSetupExceptionHandler`.

```c
#include "src/function/MulleObjCExceptionHandler.h"
#include <stdio.h>
#include <stdlib.h>

static void log_and_abort_handler(void)
{
    fprintf(stderr, ">>> Handler invoked: uncaught exception reached top of stack\n");
    abort();
}

static void risky_operation(void)
{
    id exception = (id)"CriticalFailure";
    MulleObjCRaiseException(exception);  /* Will trigger handler if uncaught */
}

int main(void)
{
    MulleObjCSetupExceptionHandler(log_and_abort_handler);
    
    /* This call raises an exception that propagates uncaught */
    risky_operation();
    
    /* Execution never reaches here */
    return 0;
}

```

## Key Source Files and Documentation

The implementation and API contracts reside in specific files within the repository:

- **[`src/function/MulleObjCExceptionHandler.h`](https://github.com/mulle-objc/mulleobjc/blob/main/src/function/MulleObjCExceptionHandler.h)**: Declares `MulleObjCSetupExceptionHandler`, `MulleObjCRaiseException`, and the NS-style wrapper functions.
- **[`dox/API_MulleObjCExceptionHandler.md`](https://github.com/mulle-objc/mulleobjc/blob/main/dox/API_MulleObjCExceptionHandler.md)**: Contains comprehensive API documentation describing the setup function, raising semantics, and thread-local behavior.

## Summary

- **`MulleObjCSetupExceptionHandler`** installs a thread-local callback for uncaught exceptions by storing the function pointer in the runtime universe structure.
- Handlers are **thread-local**; each thread must configure its own handler independently.
- Use **`MulleObjCRaiseException`** to signal errors that may trigger the custom handler if allowed to propagate uncaught.
- **NSSetUncaughtExceptionHandler** provides a familiar Objective-C wrapper around the native Mulle-ObjC API.
- Custom handlers must terminate the process (e.g., via `abort()`) to prevent undefined behavior, as the runtime does not resume normal execution after handler invocation.

## Frequently Asked Questions

### What function signature is required for MulleObjCSetupExceptionHandler?

The function expects a pointer to a callback with the signature **`void handler(void)`**, taking no parameters and returning no value. This minimal signature keeps the handler lightweight while allowing you to access thread-local storage or global state internally if needed.

### Is the exception handler global or thread-local?

The handler is **thread-local**. According to the implementation in [`src/function/MulleObjCExceptionHandler.h`](https://github.com/mulle-objc/mulleobjc/blob/main/src/function/MulleObjCExceptionHandler.h), the runtime stores the handler pointer within the per-thread universe structure. Each thread must call `MulleObjCSetupExceptionHandler` independently to configure its own handling strategy.

### How does MulleObjCRaiseException interact with the custom handler?

When **`MulleObjCRaiseException(id exception)`** is called and the exception propagates uncaught to the top of the stack, the runtime checks the current thread's handler slot. If `MulleObjCSetupExceptionHandler` was called for that thread, the runtime invokes the stored function; otherwise, it terminates the process immediately.

### Can I use NSSetUncaughtExceptionHandler instead of the MulleObjC prefix?

Yes. The header provides **`NSSetUncaughtExceptionHandler`** as a thin wrapper around `MulleObjCSetupExceptionHandler`, offering a familiar API for developers transitioning from other Objective-C runtimes. Both functions manipulate the same underlying thread-local handler slot.