Files
RedHotRoast-ios/HybridCLRData/LocalIl2CppData-OSXEditor/il2cpp/libil2cpp/os/Posix/ThreadImpl.cpp
T

325 lines
8.7 KiB
C++
Raw Normal View History

2026-07-17 14:03:00 +08:00
#include "il2cpp-config.h"
#if !IL2CPP_THREADS_STD && IL2CPP_THREADS_PTHREAD && !RUNTIME_TINY
#include <limits>
#include <unistd.h>
#include <map>
#include <pthread.h>
#include <errno.h>
#include <string.h>
#if IL2CPP_TARGET_LINUX
#include <sys/prctl.h>
#include <sys/resource.h>
#endif
#include "ThreadImpl.h"
#include "PosixHelpers.h"
namespace il2cpp
{
namespace os
{
/// An Event that we never signal. This is used for sleeping threads in an alertable state. They
/// simply wait on this object with the sleep timer as the timeout. This way we don't need a separate
/// codepath for implementing sleep logic.
static Event s_ThreadSleepObject;
#define ASSERT_CALLED_ON_CURRENT_THREAD \
IL2CPP_ASSERT(pthread_equal (pthread_self (), m_Handle) && "Must be called on current thread!");
ThreadImpl::ThreadImpl()
: m_Handle(0)
, m_StartFunc(NULL)
, m_StartArg(NULL)
, m_CurrentWaitObject(NULL)
, m_StackSize(IL2CPP_DEFAULT_STACK_SIZE)
, m_ConditionSemaphore(1)
{
}
ThreadImpl::~ThreadImpl()
{
}
ErrorCode ThreadImpl::Run(Thread::StartFunc func, void* arg, int64_t affinityMask)
{
// Store state for run wrapper.
m_StartFunc = func;
m_StartArg = arg;
// Initialize thread attributes.
pthread_attr_t attr;
int s = pthread_attr_init(&attr);
if (s)
return kErrorCodeGenFailure;
#if defined(IL2CPP_ENABLE_PLATFORM_THREAD_AFFINTY)
#if IL2CPP_THREAD_HAS_CPU_SET
if (affinityMask != Thread::kThreadAffinityAll)
{
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
for (int i = 0; i < 64; ++i)
{
if (affinityMask & (1 << i))
CPU_SET(i, &cpuset);
}
pthread_setaffinity_np(&attr, sizeof(cpu_set_t), &cpuset);
}
else
{
// set create default core affinity
pthread_attr_setaffinity_np(&attr, 0, NULL);
}
#else
pthread_attr_setaffinity_np(&attr, 0, NULL);
#endif // IL2CPP_THREAD_HAS_CPU_SET
#endif // defined(IL2CPP_ENABLE_PLATFORM_THREAD_AFFINTY)
#if defined(IL2CPP_ENABLE_PLATFORM_THREAD_STACKSIZE)
pthread_attr_setstacksize(&attr, m_StackSize);
#endif
// Create thread.
pthread_t threadId;
s = pthread_create(&threadId, &attr, &ThreadStartWrapper, this);
if (s)
return kErrorCodeGenFailure;
// Destroy thread attributes.
s = pthread_attr_destroy(&attr);
if (s)
return kErrorCodeGenFailure;
// We're up and running.
m_Handle = threadId;
return kErrorCodeSuccess;
}
void* ThreadImpl::ThreadStartWrapper(void* arg)
{
ThreadImpl* thread = reinterpret_cast<ThreadImpl*>(arg);
// Also set handle here. No matter which thread proceeds first,
// we need to make sure the handle is set.
thread->m_Handle = pthread_self();
// Detach this thread since we will manage calling Join at the VM level
// if necessary. Detaching it also prevents use from running out of thread
// handles for background threads that are never joined.
int returnValue = pthread_detach(thread->m_Handle);
IL2CPP_ASSERT(returnValue == 0);
(void)returnValue;
// Run user code.
thread->m_StartFunc(thread->m_StartArg);
return 0;
}
uint64_t ThreadImpl::Id()
{
return posix::PosixThreadIdToThreadId(m_Handle);
}
void ThreadImpl::SetName(const char* name)
{
// Can only be set on current thread on OSX and Linux.
if (pthread_self() != m_Handle)
return;
#if IL2CPP_TARGET_DARWIN
pthread_setname_np(name);
#elif IL2CPP_TARGET_LINUX || IL2CPP_TARGET_ANDROID || IL2CPP_ENABLE_PLATFORM_THREAD_RENAME
if (pthread_setname_np(m_Handle, name) == ERANGE)
{
char buf[16]; // TASK_COMM_LEN=16
strncpy(buf, name, sizeof(buf));
buf[sizeof(buf) - 1] = '\0';
pthread_setname_np(m_Handle, buf);
}
#endif
}
void ThreadImpl::SetStackSize(size_t newsize)
{
// if newsize is zero we use the per-platform default value for size of stack
if (newsize == 0)
{
newsize = IL2CPP_DEFAULT_STACK_SIZE;
}
m_StackSize = newsize;
}
int ThreadImpl::GetMaxStackSize()
{
#if IL2CPP_TARGET_DARWIN || IL2CPP_TARGET_LINUX
struct rlimit lim;
/* If getrlimit fails, we don't enforce any limits. */
if (getrlimit(RLIMIT_STACK, &lim))
return INT_MAX;
/* rlim_t is an unsigned long long on 64bits OSX but we want an int response. */
if (lim.rlim_max > (rlim_t)INT_MAX)
return INT_MAX;
return (int)lim.rlim_max;
#else
return INT_MAX;
#endif
}
void ThreadImpl::SetPriority(ThreadPriority priority)
{
////TODO
}
ThreadPriority ThreadImpl::GetPriority()
{
/// TODO
return kThreadPriorityNormal;
}
void ThreadImpl::QueueUserAPC(Thread::APCFunc function, void* context)
{
IL2CPP_ASSERT(function != NULL);
// Put on queue.
{
m_PendingAPCsMutex.Acquire();
m_PendingAPCs.push_back(APCRequest(function, context));
m_PendingAPCsMutex.Release();
}
// Interrupt an ongoing wait, only interrupt if we have an object waiting
if (m_CurrentWaitObject.load())
{
m_ConditionSemaphore.Release(1);
}
}
void ThreadImpl::CheckForUserAPCAndHandle()
{
ASSERT_CALLED_ON_CURRENT_THREAD;
m_PendingAPCsMutex.Acquire();
while (!m_PendingAPCs.empty())
{
APCRequest apcRequest = m_PendingAPCs.front();
// Remove from list. Do before calling the function to make sure the list
// is up to date in case the function throws.
m_PendingAPCs.erase(m_PendingAPCs.begin());
// Release mutex while we call the function so that we don't deadlock
// if the function starts waiting on a thread that tries queuing an APC
// on us.
m_PendingAPCsMutex.Release();
// Call function.
apcRequest.callback(apcRequest.context);
// Re-acquire mutex.
m_PendingAPCsMutex.Acquire();
}
m_PendingAPCsMutex.Release();
}
void ThreadImpl::SetWaitObject(WaitObject* waitObject)
{
// Cannot set wait objects on threads other than the current thread.
ASSERT_CALLED_ON_CURRENT_THREAD;
// This is an unprotected write as write acccess is restricted to the
// current thread.
m_CurrentWaitObject = waitObject;
}
void ThreadImpl::Sleep(uint32_t milliseconds, bool interruptible)
{
s_ThreadSleepObject.Wait(milliseconds, interruptible);
}
uint64_t ThreadImpl::CurrentThreadId()
{
return posix::PosixThreadIdToThreadId(pthread_self());
}
ThreadImpl* ThreadImpl::GetCurrentThread()
{
return Thread::GetCurrentThread()->m_Thread;
}
ThreadImpl* ThreadImpl::CreateForCurrentThread()
{
ThreadImpl* thread = new ThreadImpl();
thread->m_Handle = pthread_self();
return thread;
}
bool ThreadImpl::YieldInternal()
{
return sched_yield() == 0;
}
#if IL2CPP_HAS_NATIVE_THREAD_CLEANUP
static pthread_key_t s_CleanupKey;
static Thread::ThreadCleanupFunc s_CleanupFunc;
static void CleanupThreadIfCanceled(void* arg)
{
Thread::ThreadCleanupFunc cleanupFunc = s_CleanupFunc;
if (cleanupFunc)
cleanupFunc(arg);
}
void ThreadImpl::SetNativeThreadCleanup(Thread::ThreadCleanupFunc cleanupFunction)
{
if (cleanupFunction)
{
IL2CPP_ASSERT(!s_CleanupFunc);
s_CleanupFunc = cleanupFunction;
int result = pthread_key_create(&s_CleanupKey, &CleanupThreadIfCanceled);
IL2CPP_ASSERT(!result);
NO_UNUSED_WARNING(result);
}
else
{
IL2CPP_ASSERT(s_CleanupFunc);
int result = pthread_key_delete(s_CleanupKey);
IL2CPP_ASSERT(!result);
NO_UNUSED_WARNING(result);
s_CleanupFunc = NULL;
}
}
void ThreadImpl::RegisterCurrentThreadForCleanup(void* arg)
{
IL2CPP_ASSERT(s_CleanupFunc);
pthread_setspecific(s_CleanupKey, arg);
}
void ThreadImpl::UnregisterCurrentThreadForCleanup()
{
IL2CPP_ASSERT(s_CleanupFunc);
void* data = pthread_getspecific(s_CleanupKey);
if (data != NULL)
pthread_setspecific(s_CleanupKey, NULL);
}
#endif
}
}
#endif