Skip to main content

UnrealThread

class · AR51SDK_API C++ only singleton
class AR51SDK_API UnrealThread // hidden singleton: private ctor + Instance()

A game-thread dispatcher: it marshals work from worker/background threads onto the Unreal main (game) thread, where it is safe to touch UObjects and engine state. Implemented as a hidden singleton — you never construct it or reach for its instance directly; you call the static methods below.

This type is C++ only — none of its members are UFUNCTIONs, so nothing here is reachable from Blueprint. It exists for SDK and advanced integration C++ code that runs off the game thread (e.g. a gRPC consume loop) and needs to hop back onto it.

Pick a method by what you need:

Units

None of these methods carry positional/length values, so no cm↔m conversion applies here. (Unreal is centimeters/degrees; AR 51 mocap data is meters — conversion lives in the data/transform layer, not the threading utilities.)

Method summary

Static — threading

MethodReturns
IsMainThreadboolC++ only
Invokevoid / TC++ only · synchronous · overloads
BeginInvokeshared_ptr<TaskBase> / shared_ptr<Task<T>>C++ only · asynchronous · overloads

Static — lifecycle ⚠️ likely internal

MethodReturns
Tick / Start / Stop / ClearvoidC++ only

→ Full descriptions in Method details below.

Method details

IsMainThread

methodstaticC++ only
static bool IsMainThread();

Whether the calling code is currently running on the Unreal main/game thread.

Returnsbooltrue if called from the game thread; false from any worker thread
Exampleinferred
// Skip the marshal cost when we're already on the game thread.
auto Apply = [Char]{ Char->ApplyOnSkeletalMeshes(); };
if (UnrealThread::IsMainThread())
Apply();
else
UnrealThread::Invoke(Apply, TEXT("ApplyPose"));

Invoke

methodstaticC++ onlysynchronousoverloads
// (1) void result — blocks until done
static void Invoke(const std::function<void()>& action,
const FString& name = "Untitled");

// (2) typed result — runs on the game thread, returns its value
template<typename T>
static T Invoke(const std::function<T()>& action,
const FString& name = "Untitled");

// (3) typed result with cancellation fallback
template<typename T>
static T Invoke(const std::function<T()>& action,
const T& canceledValue);

Schedules action on the main thread and blocks the caller until it completes (a synchronous marshal). Use this from a worker thread when you must safely read or mutate UObjects / engine state and need the call to finish — or its result returned — before continuing.

  • (1) runs a void action and waits for it.
  • (2) runs an action returning T and hands back its result.
  • (3) same as (2) but, if the task is canceled (e.g. the dispatcher is not running), returns canceledValue instead of the action's result.

The optional name labels the queued task for diagnostics/logging.

Parameters
actionstd::functionthe work to run on the game thread (returning void or T)
nameFStringa label for the queued task (default "Untitled")
canceledValueToverload (3): value returned if the task is canceled
Returnsvoid / Tnothing for (1); the action's result for (2); the result or canceledValue for (3)
Exampleinferred
// From a worker thread, read engine state synchronously and use the result.
const FVector Head = UnrealThread::Invoke<FVector>(
[Char]{ return Char->GetHeadPosition(); }, // UE cm
/*canceledValue*/ FVector::ZeroVector);
caution

Invoke blocks. Never call it from the game thread waiting on the game thread — that deadlocks. Guard with IsMainThread (run the work inline if already on the game thread).

BeginInvoke

methodstaticC++ onlyasynchronousoverloads
// (1) void result — returns an untyped handle
static std::shared_ptr<TaskBase> BeginInvoke(const std::function<void()>& action,
const FString& name = "Untitled");

// (2) typed result — returns a typed handle carrying T
template<typename T>
static std::shared_ptr<Task<T>> BeginInvoke(const std::function<T()>& action,
const FString& name = "Untitled");

Fire-and-forget asynchronous dispatch: queues action to run on the main thread and returns immediately with a task handle you can await or inspect later — TaskBase for the void overload, Task<T> for the typed one. If the dispatcher is not running, the typed overload returns an already-canceled task.

Parameters
actionstd::functionthe work to run on the game thread (returning void or T)
nameFStringa label for the queued task (default "Untitled")
Returnsshared_ptr<TaskBase> / shared_ptr<Task<T>>a handle to await/inspect the queued work (see Task handles)
Exampleinferred
// Queue work on the game thread without blocking; grab the result when ready.
auto Task = UnrealThread::BeginInvoke<int32>(
[Consumer]{ return Consumer->GetCharacters().Num(); });

// ... later, on any thread:
Task->Wait();
if (Task->IsCompleted() && !Task->IsCanceled())
const int32 Count = Task->GetResult();

Task handles

propertyC++ onlyreturn types
std::shared_ptr<TaskBase> // returned by the void BeginInvoke
std::shared_ptr<Task<T>> // returned by the typed BeginInvoke

The lightweight handles returned by BeginInvoke. You consume these as return values; you do not construct them (their constructors and the Run/Cancel machinery are internal). Both expose:

  • bool IsCompleted() — the task has finished running.
  • bool IsCanceled() — the task was canceled (e.g. the dispatcher was not running) and never produced a result.
  • void Wait() — block the caller until the task completes (or is canceled).
  • GetResult() — on Task<T>, the action's T result (valid once completed and not canceled).

Lifecycle

methodstaticC++ only
static void Tick(); // drain + run queued tasks on the game thread
static void Start(); // enable processing
static void Stop(); // disable processing
static void Clear(); // empty the queue

Dispatcher pump / lifecycle controls. Tick() drains the queue and runs pending tasks on the main thread (typically once per frame); Start/Stop enable/disable processing; Clear empties the queue.

⚠️ Likely internal

The SDK is expected to pump these itself (e.g. driving Tick() from its per-frame loop). Integrators normally call only IsMainThread, Invoke, and BeginInvoke. Calling Stop/Clear from game code can strand or drop queued work. ⚠️ needs confirmation that any of these are intended for integrator use.

See also

Was this page helpful?