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