UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
AndroidSignals.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3/*=============================================================================
4 AndroidSignals.h
5=============================================================================*/
7#include "CoreGlobals.h"
9#include <sys/syscall.h>
10
11#pragma once
12
13#if ANDROID_HAS_RTSIGNALS
14
15#define THREAD_CALLSTACK_GENERATOR SIGRTMIN + 5
16#define FATAL_SIGNAL_FWD SIGRTMIN + 6
17#define THREADBACKTRACE_SIGNAL_FWD SIGRTMIN + 7
18
19/*
20 This is a helper class that attempts to avoid android's limited signal stack size by
21 using a thread that infinite sleeps and is woken to only process signals.
22 'Target' signals are caught and forwarded to the sleeping thread.
23 The forwarded signal runs on the sleeping thread calling Derived::HandleTargetSignal() with the originating signal's parameters.
24
25 The crash handler therefore, does not run on the same thread that originally received the target signal.
26
27 Note, to avoid the requirement of suspending the thread while backgrounding (and therefore missing signals) the thread sleeps infinitely.
28*/
29
30extern float GAndroidSignalTimeOut;
31
32template<typename Derived>
34{
35protected:
36 enum class ESignalType : int32 { Signal, Exit };
38
39 static void Init(int ForwardingSignal)
40 {
42 check(SignalThreadStatus == (int32)ESignalThreadStatus::NotInitialized);
43
46
50 const size_t StackSize = 256 * 1024;
51 bool bStackSizeSuccess = (pthread_attr_setstacksize(&otherAttr, StackSize) == 0);
52 UE_CLOG(!bStackSizeSuccess, LogAndroid, Error, TEXT("Failed to set signal thread stack size."));
53
55 UE_CLOG(!bThreadCreated, LogAndroid, Fatal, TEXT("Failed to create signal handler"));
56
57 while (FPlatformAtomics::AtomicRead(&SignalThreadStatus) != (int32)ESignalThreadStatus::Ready)
58 {
59 FPlatformProcess::SleepNoStats(0);
60 }
61
63
69 }
70
71 static void Release()
72 {
73 if (ForwardingSignalType != -1)
74 {
76 while (FPlatformAtomics::AtomicRead(&SignalThreadStatus) != (int32)ESignalThreadStatus::Exited)
77 {
78 FPlatformProcess::SleepNoStats(0);
79 }
80
82
83 SignalThreadStatus = (int32)ESignalThreadStatus::NotInitialized;
84 ForwardingThreadID = 0xffffffff;
86 }
87 }
88
89 static void ForwardSignal(int InSignal, siginfo* InInfo, void* InContext)
90 {
91 while (FPlatformAtomics::InterlockedCompareExchange(&SignalThreadStatus, (int32)ESignalThreadStatus::Starting, (int32)ESignalThreadStatus::Ready) != (int32)ESignalThreadStatus::Ready)
92 {
93 FPlatformProcess::SleepNoStats(0.0f);
94 }
95 SignalParams.Signal = InSignal;
97 SignalParams.Info = InInfo;
98 SignalParams.Context = InContext;
99
101
102 if (SendSignal((int)ESignalType::Signal))
103 {
105 }
106 else
107 {
108 // did not send, thread not available... set status to ready.
109 FPlatformAtomics::AtomicStore(&SignalThreadStatus, (int32)ESignalThreadStatus::Ready);
110 }
111 }
112
113private:
114 static void* ThreadFunc(void* param)
115 {
117 FPlatformAtomics::AtomicStore(&SignalThreadStatus, (int32)ESignalThreadStatus::Ready);
118 FPlatformProcess::SleepInfinite();
119 return nullptr;
120 }
121
122 static bool SendSignal(int SignalType)
123 {
124 siginfo_t info;
125 memset(&info, 0, sizeof(siginfo_t));
126 info.si_signo = ForwardingSignalType;
127 info.si_code = SI_QUEUE;
128 info.si_pid = syscall(SYS_getpid);
129 info.si_uid = syscall(SYS_getuid);
130 info.si_value.sival_int = SignalType;
131
132 // Avoid using sigqueue here as if the ThreadId is already blocked and in a signal handler
133 // sigqueue will try a different thread signal handler and report the wrong callstack
134 return syscall(SYS_rt_tgsigqueueinfo, info.si_pid, ForwardingThreadID, ForwardingSignalType, &info) == 0;
135 }
136
137 static bool SendExitSignal()
138 {
139 // enqueue a signal to exit the signal thread.
140 return SendSignal((int)ESignalType::Exit);
141 }
142
144 {
145 const float PollInterval = 0.01f;
146 float CurrentWaitTime = 0.0f;
147
148 while (FPlatformAtomics::InterlockedCompareExchange(&SignalThreadStatus, (int32)ESignalThreadStatus::Ready, (int32)ESignalThreadStatus::Complete) != (int32)ESignalThreadStatus::Complete)
149 {
150 // have we timed out?
152 {
153 exit(0);
154 }
155 FPlatformProcess::SleepNoStats(PollInterval);
157 }
158 }
159
160 static void OnForwardedTargetSignal(int Signal, siginfo* Info, void* Context)
161 {
162 FPlatformAtomics::AtomicStore(&SignalThreadStatus, (int32)ESignalThreadStatus::Busy);
163
164 if (Info->si_value.sival_int == (int)ESignalType::Exit)
165 {
166 FPlatformAtomics::AtomicStore(&SignalThreadStatus, (int32)ESignalThreadStatus::Exited);
167 pthread_exit(0);
168 return;
169 }
170 Derived::HandleTargetSignal(SignalParams.Signal, SignalParams.Info, SignalParams.Context, SignalParams.ThreadID);
171 FPlatformAtomics::AtomicStore(&SignalThreadStatus, (int32)ESignalThreadStatus::Complete);
172 }
173
174 struct FSignalParams
175 {
176 int Signal;
177 uint32 ThreadID;
178 siginfo* Info;
179 void* Context;
180 };
181
187};
188#endif
#define check(expr)
Definition AssertionMacros.h:314
#define TEXT(x)
Definition Platform.h:1272
FPlatformTypes::int32 int32
A 32-bit signed integer.
Definition Platform.h:1125
UE_FORCEINLINE_HINT TSharedRef< CastToType, Mode > StaticCastSharedRef(TSharedRef< CastFromType, Mode > const &InSharedRef)
Definition SharedPointer.h:127
void Init()
Definition LockFreeList.h:4
#define UE_CLOG(Condition, CategoryName, Verbosity, Format,...)
Definition LogMacros.h:298
uint32_t uint32
Definition binka_ue_file_header.h:6
static UE_FORCEINLINE_HINT void MemoryBarrier()
Definition AndroidPlatformMisc.h:249
static uint32 GetCurrentThreadId(void)
Definition AndroidPlatformTLS.h:20
static UE_FORCEINLINE_HINT void * Memzero(void *Dest, SIZE_T Count)
Definition UnrealMemory.h:131