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

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. The function accepts a single parameter: a pointer to a handler function that matches the signature void handler(void).

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.

#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.

/* 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.

#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:

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, 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.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →