You can start Ultralight's background threads through an engine's thread API by implementing `ThreadFactory`. This lets you name threads, set priorities, configure CPU affinity, and track background workers in an engine profiler. The factory is available in every edition, but it's optional— most applications don't need one.

## The Default Thread Factory

AppCore doesn't install a thread factory. Without one, `App::Create()` and `Renderer::Create()` start threads directly using `_beginthreadex()` on Windows or `pthread_create()` on POSIX.

Setting a custom factory on `Platform` before creating the `App` or `Renderer` overrides this default. For setup details, see [Setting Up the Platform](/docs/2.0/setting-up-the-platform).

## Threads the Factory Covers

The factory creates the threads that run page content. This includes script workers, the JavaScript compiler, garbage collection, networking, and resource loading.

> 📘 Threads Started Outside the Factory
>
> The factory does not manage every thread in the process. Media playback, some background rendering tasks, and linked third-party libraries start their own threads directly.

## Implementing CreateThread()

Subclass `ThreadFactory` and override `CreateThread()`. Each call has one job— start the requested thread and report its identifiers back to the library.

### Starting the Thread

Start a thread that executes `entry_point(entry_point_data)`.

Return `true` if the thread started successfully, or `false` if creation failed.

### Using Thread Name and Type

The `name` parameter provides a label for the thread, which can be `nullptr`.

The `type` parameter indicates the thread's role, such as `ThreadType::JavaScript`, `ThreadType::Network`, or `ThreadType::GarbageCollection`. You can use this hint to set thread priority, assign CPU affinity, or tag threads in a profiler.

### Reporting the ID and Handle

Before returning from `CreateThread()`, fill `result` with the thread's identifiers.

#### Reporting on Windows

Set `result.id` to the thread's numeric identifier— either the ID written by `_beginthreadex()`, or what `GetCurrentThreadId()` returns inside the thread.

Set `result.handle` to the `HANDLE` returned by `_beginthreadex()`.

#### Reporting on POSIX

Set `result.id` to any unique integer you choose.

Set `result.handle` to the thread's `pthread_t` handle.

## Registering the Factory

To register the factory, pass an instance to `Platform::instance().set_thread_factory()` before creating the `Renderer`:

```cpp
#include <Ultralight/Ultralight.h>

using namespace ultralight;

class MyThreadFactory : public ThreadFactory {
 public:
  bool CreateThread(const char* name, ThreadType type,
                    ThreadEntryPoint entry_point, void* entry_point_data,
                    CreateThreadResult& result) override {
    ///
    /// Start a thread that calls entry_point(entry_point_data), naming it after
    /// name and picking a priority from type.
    ///
    // Pseudo-code, spawn the thread through your engine's thread API here.

    ///
    /// Report the id and handle the library will use to refer to it later.
    ///
    result.id = 0;      // Pseudo-code, the id (what GetCurrentThreadId()
                        // reports on Windows).
    result.handle = 0;  // Pseudo-code, the OS handle (HANDLE / pthread_t).
    return true;
  }
};

MyThreadFactory my_thread_factory;

void InitThreads() {
  ///
  /// Install the factory before creating the Renderer.
  ///
  Platform::instance().set_thread_factory(&my_thread_factory);
}
```

For handler lifetime requirements and registration timing, see [Setting Up the Platform](/docs/2.0/setting-up-the-platform).
