[add] first

This commit is contained in:
2023-10-08 10:24:48 +08:00
commit b1ae0510a9
1048 changed files with 3254361 additions and 0 deletions

View File

@@ -0,0 +1,30 @@
#pragma once
#if IL2CPP_THREADS_PTHREAD && !RUNTIME_TINY
#include <pthread.h>
#include "utils/NonCopyable.h"
class FastMutexImpl;
namespace il2cpp
{
namespace os
{
class ConditionVariableImpl : public il2cpp::utils::NonCopyable
{
public:
ConditionVariableImpl();
~ConditionVariableImpl();
int Wait(FastMutexImpl* lock);
int TimedWait(FastMutexImpl* lock, uint32_t timeout_ms);
void Broadcast();
void Signal();
private:
pthread_cond_t m_ConditionVariable;
};
}
}
#endif

View File

@@ -0,0 +1,15 @@
#pragma once
#include <string>
#include <stdint.h>
#include "os/ErrorCodes.h"
namespace il2cpp
{
namespace os
{
ErrorCode SocketErrnoToErrorCode(int32_t code);
ErrorCode FileErrnoToErrorCode(int32_t code);
ErrorCode PathErrnoToErrorCode(const std::string& path, int32_t code);
}
}

View File

@@ -0,0 +1,26 @@
#pragma once
#if IL2CPP_THREADS_PTHREAD && !RUNTIME_TINY
#include "os/ErrorCodes.h"
#include "os/WaitStatus.h"
#include "PosixWaitObject.h"
#include <pthread.h>
namespace il2cpp
{
namespace os
{
class EventImpl : public posix::PosixWaitObject
{
public:
EventImpl(bool manualReset = false, bool signaled = false);
ErrorCode Set();
ErrorCode Reset();
};
}
}
#endif

View File

@@ -0,0 +1,62 @@
#pragma once
#include "il2cpp-config.h"
#if IL2CPP_THREADS_PTHREAD && IL2CPP_SUPPORT_THREADS
#include <pthread.h>
#include "MutexImpl.h"
namespace il2cpp
{
namespace os
{
// The pthread reader writer lock takes a lock to update its state on lock and unlock
// So if you're read case time is on the order of taking a lock, then it's faster
// to just use a non-recursive mutex
class FastReaderReaderWriterLockImpl
{
public:
FastReaderReaderWriterLockImpl()
{
pthread_mutex_init(&m_Mutex, NULL);
}
~FastReaderReaderWriterLockImpl()
{
pthread_mutex_destroy(&m_Mutex);
}
void LockExclusive()
{
pthread_mutex_lock(&m_Mutex);
}
void LockShared()
{
pthread_mutex_lock(&m_Mutex);
}
void ReleaseExclusive()
{
pthread_mutex_unlock(&m_Mutex);
}
void ReleaseShared()
{
pthread_mutex_unlock(&m_Mutex);
}
pthread_mutex_t* GetOSHandle()
{
return &m_Mutex;
}
private:
pthread_mutex_t m_Mutex;
};
}
}
#endif

View File

@@ -0,0 +1,49 @@
#pragma once
#include "il2cpp-config.h"
#if IL2CPP_TARGET_POSIX
#include <string>
#include <sys/stat.h>
#include <sys/types.h>
#include "os/File.h"
#include "os/c-api/OSGlobalEnums.h"
namespace il2cpp
{
namespace os
{
struct FileHandle
{
int fd;
FileType type;
std::string path;
int options;
int shareMode;
int accessMode;
// The default value of this field should be false,
// meaning we _do_ own the file descriptor, and therefore
// can close it. Zero-allocating this struct is something
// we want to support, so make sure the default is 0.
bool doesNotOwnFd;
// device and inode are used as key for finding file handles
dev_t device;
ino_t inode;
// Linked list of file handles
FileHandle *prev;
FileHandle *next;
FileHandle()
: fd(-1), type(kFileTypeUnknown), options(0), shareMode(0), accessMode(0),
doesNotOwnFd(false), device(0), inode(0), prev(NULL), next(NULL)
{
}
};
}
}
#endif

View File

@@ -0,0 +1,73 @@
#pragma once
#if IL2CPP_THREADS_PTHREAD && !RUNTIME_TINY
#include "os/ErrorCodes.h"
#include "os/WaitStatus.h"
#include "PosixWaitObject.h"
#include <pthread.h>
namespace il2cpp
{
namespace os
{
class Thread;
class MutexImpl : public posix::PosixWaitObject
{
public:
MutexImpl();
void Lock(bool interruptible);
bool TryLock(uint32_t milliseconds, bool interruptible);
void Unlock();
private:
/// Thread that currently owns the object. Used for recursion checks.
Thread* m_OwningThread;
/// Number of recursive locks on the owning thread.
uint32_t m_RecursionCount;
};
class FastMutexImpl
{
public:
FastMutexImpl()
{
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&m_Mutex, &attr);
pthread_mutexattr_destroy(&attr);
}
~FastMutexImpl()
{
pthread_mutex_destroy(&m_Mutex);
}
void Lock()
{
pthread_mutex_lock(&m_Mutex);
}
void Unlock()
{
pthread_mutex_unlock(&m_Mutex);
}
pthread_mutex_t* GetOSHandle()
{
return &m_Mutex;
}
private:
pthread_mutex_t m_Mutex;
};
}
}
#endif

View File

@@ -0,0 +1,38 @@
#pragma once
#if !IL2CPP_USE_NETWORK_ACCESS_HANDLER
#include <stdint.h>
namespace il2cpp
{
namespace os
{
// Define a stub for platforms that don't require requesting access to network hardware
class NetworkAccessHandler
{
public:
typedef int SocketDescriptor;
void InheritNetworkAccessState(SocketDescriptor _fd) {}
void CancelNetworkAccess() {}
bool RequestNetwork(SocketDescriptor _fd, const struct sockaddr *sa = NULL, int32_t sa_size = 0) { return true; }
bool PrepareForBind(SocketDescriptor _fd, const struct sockaddr *sa = NULL, int32_t sa_size = 0) { return true; }
#if IL2CPP_SUPPORT_IPV6
bool PrepareForBind(SocketDescriptor _fd, const struct sockaddr_in6* sa = NULL, int32_t sa_size = 0) { return true; }
#endif
bool PrepareForConnect(SocketDescriptor _fd, const struct sockaddr *sa = NULL, int32_t sa_size = 0) { return true; }
bool WaitForNetworkStatus(SocketDescriptor _fd, bool isConnect = false) { return true; }
int32_t GetError() { return 0; }
class Auto
{
public:
bool RequestAccessForAddressInfo(bool isLocalNetworkMode = false) { return true; }
int32_t GetError() { return 0; }
};
};
}
}
#endif

View File

@@ -0,0 +1,65 @@
#pragma once
#if (IL2CPP_TARGET_POSIX || IL2CPP_SUPPORT_SOCKETS_POSIX_API) && !RUNTIME_TINY
#include <pthread.h>
#include <time.h>
#include <sys/poll.h>
#include "os/Thread.h"
#include "os/Socket.h"
namespace il2cpp
{
namespace os
{
namespace posix
{
inline timespec Ticks100NanosecondsToTimespec(int64_t ticks)
{
timespec result;
result.tv_sec = ticks / 10000000;
result.tv_nsec = (ticks % 10000000) * 100;
return result;
}
inline timespec MillisecondsToTimespec(uint32_t ms)
{
timespec result;
result.tv_sec = ms / 1000;
result.tv_nsec = (ms % 1000) * 1000000;
return result;
}
inline Thread::ThreadId PosixThreadIdToThreadId(pthread_t thread)
{
Thread::ThreadId threadId = 0;
memcpy(&threadId, &thread, std::min(sizeof(threadId), sizeof(thread)));
return threadId;
}
struct PosixAutoLock
{
pthread_mutex_t* mutex;
PosixAutoLock(pthread_mutex_t* m)
: mutex(m) { pthread_mutex_lock(mutex); }
~PosixAutoLock()
{ pthread_mutex_unlock(mutex); }
};
inline short PollFlagsToPollEvents(PollFlags flags)
{
return (short)flags;
}
inline PollFlags PollEventsToPollFlags(short events)
{
return (PollFlags)events;
}
int Poll(pollfd* handles, int numHandles, int timeout);
}
}
}
#endif // IL2CPP_TARGET_POSIX

View File

@@ -0,0 +1,97 @@
#pragma once
#if IL2CPP_TARGET_POSIX && !RUNTIME_TINY
#include <pthread.h>
#include <stdint.h>
#include <limits.h>
#include "utils/NonCopyable.h"
#include "os/WaitStatus.h"
#if (IL2CPP_USE_POSIX_COND_TIMEDWAIT_REL)
int pthread_cond_timedwait_relative_np(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *spec);
#endif
namespace il2cpp
{
namespace os
{
class ThreadImpl;
namespace posix
{
const uint32_t kNoTimeout = UINT_MAX;
////TODO: generalize this so that it can be used with c++11 condition variables
/// Base class for all synchronization primitives when running on POSIX.
///
/// To support interruption and timeouts for all synchronization primitives (events, mutexes, and
/// semaphores) we implement these primitives ourselves instead of using their standard POSIX/Mach
/// system counterparts. See PosixWaitObject.cpp for an explanation why.
class PosixWaitObject : public il2cpp::utils::NonCopyable
{
public:
~PosixWaitObject();
WaitStatus Wait(bool interruptible = false);
WaitStatus Wait(uint32_t ms, bool interruptible = false);
/// Cause an ongoing blocking wait on this object to exit and check for pending APCs.
/// If the object is not currently being waited on, will cause the next wait to exit
/// right away and check for APCs. After APCs have been handled, the object will go
/// back to waiting except if the wait timeout has expired.
void InterruptWait();
void* GetOSHandle();
static void LockWaitObjectDeletion();
static void UnlockWaitObjectDeletion();
protected:
enum Type
{
kMutex, /// All mutexes are recursive.
kManualResetEvent,
kAutoResetEvent,
kSemaphore
};
PosixWaitObject(Type type);
Type m_Type;
/// Always have to acquire this mutex to touch m_Count.
pthread_mutex_t m_Mutex;
/// Signal other threads of changes to m_Count.
pthread_cond_t m_Condition;
/// "Release" count for the primitive. Means different things depending on the type of primitive
/// but for all primitives, we wait until this is zero. Semaphores are the only primitive for which
/// this can go past 1.
uint32_t m_Count;
/// Number of threads waiting on this object. This is used to prevent unnecessary signals
/// on m_Condition.
uint32_t m_WaitingThreadCount;
bool HaveWaitingThreads() const { return (m_WaitingThreadCount != 0); }
};
struct AutoLockWaitObjectDeletion
{
AutoLockWaitObjectDeletion() { PosixWaitObject::LockWaitObjectDeletion(); }
~AutoLockWaitObjectDeletion() { PosixWaitObject::UnlockWaitObjectDeletion(); }
};
}
}
}
#endif // IL2CPP_TARGET_POSIX

View File

@@ -0,0 +1,70 @@
#pragma once
#include "il2cpp-config.h"
#if IL2CPP_THREADS_PTHREAD && IL2CPP_SUPPORT_THREADS
#include <pthread.h>
namespace il2cpp
{
namespace os
{
class ReaderWriterLockImpl
{
public:
ReaderWriterLockImpl()
{
int result = pthread_rwlock_init(&m_Lock, NULL);
NO_UNUSED_WARNING(result);
IL2CPP_ASSERT(result == 0);
}
~ReaderWriterLockImpl()
{
int result = pthread_rwlock_destroy(&m_Lock);
NO_UNUSED_WARNING(result);
IL2CPP_ASSERT(result == 0);
}
void LockExclusive()
{
int result = pthread_rwlock_wrlock(&m_Lock);
NO_UNUSED_WARNING(result);
IL2CPP_ASSERT(result == 0);
}
void LockShared()
{
int result = pthread_rwlock_rdlock(&m_Lock);
NO_UNUSED_WARNING(result);
IL2CPP_ASSERT(result == 0);
}
void ReleaseExclusive()
{
int result = pthread_rwlock_unlock(&m_Lock);
NO_UNUSED_WARNING(result);
IL2CPP_ASSERT(result == 0);
}
void ReleaseShared()
{
int result = pthread_rwlock_unlock(&m_Lock);
NO_UNUSED_WARNING(result);
IL2CPP_ASSERT(result == 0);
}
pthread_rwlock_t* GetOSHandle()
{
return &m_Lock;
}
private:
pthread_rwlock_t m_Lock;
};
}
}
#endif

View File

@@ -0,0 +1,28 @@
#pragma once
#if IL2CPP_THREADS_PTHREAD && !RUNTIME_TINY
#include "PosixWaitObject.h"
#include "os/ErrorCodes.h"
#include "os/WaitStatus.h"
#include <stdint.h>
namespace il2cpp
{
namespace os
{
class SemaphoreImpl : public posix::PosixWaitObject
{
public:
SemaphoreImpl(int32_t initialValue, int32_t maximumValue);
bool Post(int32_t releaseCount, int32_t* previousCount);
protected:
uint32_t m_MaximumValue;
};
}
}
#endif

View File

@@ -0,0 +1,146 @@
#pragma once
#if (IL2CPP_TARGET_POSIX || IL2CPP_SUPPORT_SOCKETS_POSIX_API) && !RUNTIME_TINY
#include <string>
#include <vector>
#include <stdint.h>
#include <sys/socket.h>
#include "os/Socket.h"
#include "os/ErrorCodes.h"
#include "os/WaitStatus.h"
#include "utils/Expected.h"
#include "utils/NonCopyable.h"
#if IL2CPP_USE_NETWORK_ACCESS_HANDLER
#include "os/NetworkAccessHandler.h"
#else
#include "NetworkAccessHandlerStub.h"
#endif
struct sockaddr;
namespace il2cpp
{
namespace os
{
class SocketImpl : public il2cpp::utils::NonCopyable
{
public:
typedef int SocketDescriptor;
SocketImpl(ThreadStatusCallback thread_status_callback);
~SocketImpl();
inline SocketDescriptor GetDescriptor()
{
return _fd;
}
ErrorCode GetLastError() const;
WaitStatus Create(SocketDescriptor fd, int32_t family, int32_t type, int32_t protocol);
WaitStatus Create(AddressFamily family, SocketType type, ProtocolType protocol);
WaitStatus Close();
bool IsClosed()
{
return (_fd == -1);
}
WaitStatus SetBlocking(bool blocking);
WaitStatus Listen(int32_t blacklog);
WaitStatus Bind(const char *path);
WaitStatus Bind(const char *address, uint16_t port);
WaitStatus Bind(uint32_t address, uint16_t port);
utils::Expected<WaitStatus> Bind(uint8_t address[ipv6AddressSize], uint32_t scope, uint16_t port);
WaitStatus Connect(const char *path);
WaitStatus Connect(uint32_t address, uint16_t port);
utils::Expected<WaitStatus> Connect(uint8_t address[ipv6AddressSize], uint32_t scope, uint16_t port);
WaitStatus Disconnect(bool reuse);
WaitStatus Shutdown(int32_t how);
WaitStatus GetLocalEndPointInfo(EndPointInfo &info);
WaitStatus GetRemoteEndPointInfo(EndPointInfo &info);
WaitStatus Receive(const uint8_t *data, int32_t count, os::SocketFlags flags, int32_t *len);
WaitStatus ReceiveFromInternal(const uint8_t *data, size_t count, int32_t flags, int32_t *len, struct sockaddr *from, int32_t *fromlen);
WaitStatus Send(const uint8_t *data, int32_t count, os::SocketFlags flags, int32_t *len);
WaitStatus SendArray(WSABuf *wsabufs, int32_t count, int32_t *sent, SocketFlags c_flags);
WaitStatus ReceiveArray(WSABuf *wsabufs, int32_t count, int32_t *len, SocketFlags c_flags);
WaitStatus SendTo(uint32_t address, uint16_t port, const uint8_t *data, int32_t count, os::SocketFlags flags, int32_t *len);
WaitStatus SendTo(const char *path, const uint8_t *data, int32_t count, os::SocketFlags flags, int32_t *len);
utils::Expected<WaitStatus> SendTo(uint8_t address[ipv6AddressSize], uint32_t scope, uint16_t port, const uint8_t *data, int32_t count, os::SocketFlags flags, int32_t *len);
WaitStatus RecvFrom(uint32_t address, uint16_t port, const uint8_t *data, int32_t count, os::SocketFlags flags, int32_t *len, os::EndPointInfo &ep);
WaitStatus RecvFrom(const char *path, const uint8_t *data, int32_t count, os::SocketFlags flags, int32_t *len, os::EndPointInfo &ep);
utils::Expected<WaitStatus> RecvFrom(uint8_t address[ipv6AddressSize], uint32_t scope, uint16_t port, const uint8_t *data, int32_t count, os::SocketFlags flags, int32_t *len, os::EndPointInfo &ep);
WaitStatus Available(int32_t *amount);
WaitStatus Accept(os::Socket **socket);
WaitStatus Ioctl(int32_t command, const uint8_t *in_data, int32_t in_len, uint8_t *out_data, int32_t out_len, int32_t *written);
WaitStatus GetSocketOption(SocketOptionLevel level, SocketOptionName name, uint8_t *buffer, int32_t *length);
WaitStatus GetSocketOptionFull(SocketOptionLevel level, SocketOptionName name, int32_t *first, int32_t *second);
WaitStatus SetSocketOption(SocketOptionLevel level, SocketOptionName name, int32_t value);
WaitStatus SetSocketOptionLinger(SocketOptionLevel level, SocketOptionName name, bool enabled, int32_t seconds);
WaitStatus SetSocketOptionArray(SocketOptionLevel level, SocketOptionName name, const uint8_t *buffer, int32_t length);
WaitStatus SetSocketOptionMembership(SocketOptionLevel level, SocketOptionName name, uint32_t group_address, uint32_t local_address);
#if IL2CPP_SUPPORT_IPV6
WaitStatus SetSocketOptionMembership(SocketOptionLevel level, SocketOptionName name, IPv6Address ipv6, uint64_t interfaceOffset);
#endif
#if IL2CPP_SUPPORT_IPV6_SUPPORT_QUERY
static bool IsIPv6Supported();
#endif
WaitStatus SendFile(const char *filename, TransmitFileBuffers *buffers, TransmitFileOptions options);
static WaitStatus Poll(std::vector<PollRequest> &requests, int32_t count, int32_t timeout, int32_t *result, int32_t *error);
static WaitStatus Poll(std::vector<PollRequest> &requests, int32_t timeout, int32_t *result, int32_t *error);
static WaitStatus Poll(PollRequest& request, int32_t timeout, int32_t *result, int32_t *error);
static WaitStatus GetHostName(std::string &name);
static WaitStatus GetHostByName(const std::string &host, std::string &name, std::vector<std::string> &aliases, std::vector<std::string> &addresses);
static WaitStatus GetHostByName(const std::string &host, std::string &name, int32_t &family, std::vector<std::string> &aliases, std::vector<void*> &addr_list, int32_t &addr_size);
static WaitStatus GetHostByAddr(const std::string &address, std::string &name, std::vector<std::string> &aliases, std::vector<std::string> &addr_list);
static void Startup();
static void Cleanup();
static bool is_private(const struct sockaddr *sa, socklen_t sa_size);
static bool is_private(const char* address);
private:
bool _is_valid;
SocketDescriptor _fd;
int32_t _domain;
int32_t _type;
int32_t _protocol;
ErrorCode _saved_error;
int32_t _still_readable;
ThreadStatusCallback _thread_status_callback;
NetworkAccessHandler _networkAccess;
void StoreLastError();
void StoreLastError(int32_t error_no);
WaitStatus ConnectInternal(struct sockaddr *sa, int32_t sa_size);
WaitStatus SendToInternal(struct sockaddr *sa, int32_t sa_size, const uint8_t *data, int32_t count, os::SocketFlags flags, int32_t *len);
WaitStatus SetSocketOptionInternal(int32_t level, int32_t name, const void *value, int32_t len);
};
}
}
#endif

View File

@@ -0,0 +1,112 @@
#pragma once
#if !IL2CPP_THREADS_STD && IL2CPP_THREADS_PTHREAD && !RUNTIME_TINY
#include <pthread.h>
#include <vector>
#include <atomic>
#include "PosixWaitObject.h"
#include "os/ErrorCodes.h"
#include "os/Mutex.h"
#include "os/Event.h"
#include "os/Thread.h"
#include "os/WaitStatus.h"
#include "utils/NonCopyable.h"
#if defined(IL2CPP_ENABLE_PLATFORM_THREAD_AFFINTY)
struct cpu_set_t;
int pthread_attr_setaffinity_np(pthread_attr_t *attr, size_t cpusetsize, const cpu_set_t *cpuset);
#endif
#if defined(IL2CPP_ENABLE_PLATFORM_THREAD_RENAME)
int pthread_setname_np(pthread_t handle, const char *name);
#endif
#if !defined(IL2CPP_DEFAULT_STACK_SIZE)
#define IL2CPP_DEFAULT_STACK_SIZE ( 1 * 1024 * 1024) // default .NET stacksize is 1mb
#endif
namespace il2cpp
{
namespace os
{
/// POSIX threads implementation. Supports APCs and interruptible waits.
class ThreadImpl : public il2cpp::utils::NonCopyable
{
public:
ThreadImpl();
~ThreadImpl();
uint64_t Id();
ErrorCode Run(Thread::StartFunc func, void* arg, int64_t affinityMask);
void QueueUserAPC(Thread::APCFunc func, void* context);
void SetName(const char* name);
void SetPriority(ThreadPriority priority);
ThreadPriority GetPriority();
void SetStackSize(size_t newsize);
static int GetMaxStackSize();
/// Handle any pending APCs.
/// NOTE: Can only be called on current thread.
void CheckForUserAPCAndHandle();
static void Sleep(uint32_t milliseconds, bool interruptible);
static uint64_t CurrentThreadId();
static ThreadImpl* GetCurrentThread();
static ThreadImpl* CreateForCurrentThread();
static bool YieldInternal();
#if IL2CPP_HAS_NATIVE_THREAD_CLEANUP
static void SetNativeThreadCleanup(Thread::ThreadCleanupFunc cleanupFunction);
static void RegisterCurrentThreadForCleanup(void* arg);
static void UnregisterCurrentThreadForCleanup();
#endif
private:
friend class posix::PosixWaitObject; // SetWaitObject(), CheckForAPCAndHandle()
std::atomic<pthread_t> m_Handle;
/// The synchronization primitive that this thread is currently blocked on.
///
/// NOTE: This field effectively turns these wait object into shared resources -- which makes deletion
/// a tricky affair. To avoid one thread trying to interrupt a wait while the other thread already
/// is in progress of deleting the wait object, we use a global mutex in PosixWaitObject.cpp that
/// must be locked by any thread trying to trigger an interrupt.
posix::PosixWaitObject* m_CurrentWaitObject;
/// Start data.
Thread::StartFunc m_StartFunc;
void* m_StartArg;
/// List of APC requests for this thread.
struct APCRequest
{
Thread::APCFunc callback;
void* context;
APCRequest(Thread::APCFunc callback, void* context) :
callback(callback), context(context)
{
}
};
pthread_mutex_t m_PendingAPCsMutex;
std::vector<APCRequest> m_PendingAPCs;
size_t m_StackSize; // size of stack (can not be adjusted after thread creation)
/// Set the synchronization object the thread is about to wait on.
/// NOTE: This can only be called on the current thread.
void SetWaitObject(posix::PosixWaitObject* waitObject);
static void* ThreadStartWrapper(void* arg);
};
}
}
#endif

View File

@@ -0,0 +1,118 @@
#pragma once
#if IL2CPP_THREADS_PTHREAD
#include "os/ErrorCodes.h"
#include "utils/NonCopyable.h"
#include <pthread.h>
namespace il2cpp
{
namespace os
{
class ThreadLocalValueImpl : public il2cpp::utils::NonCopyable
{
public:
inline ThreadLocalValueImpl()
{
pthread_key_t key;
int result = pthread_key_create(&key, NULL);
IL2CPP_ASSERT(result == 0);
NO_UNUSED_WARNING(result);
m_Key = key;
}
inline ~ThreadLocalValueImpl()
{
int result = pthread_key_delete(m_Key);
IL2CPP_ASSERT(result == 0);
NO_UNUSED_WARNING(result);
}
inline ErrorCode SetValue(void* value)
{
#if IL2CPP_TARGET_DARWIN
apple_pthread_setspecific_direct(m_Key, value);
#else
if (pthread_setspecific(m_Key, value))
return kErrorCodeGenFailure;
#endif
return kErrorCodeSuccess;
}
inline ErrorCode GetValue(void** value)
{
#if IL2CPP_TARGET_DARWIN
*value = apple_pthread_getspecific_direct(m_Key);
#else
*value = pthread_getspecific(m_Key);
#endif
return kErrorCodeSuccess;
}
private:
#if IL2CPP_TARGET_DARWIN
static inline void * apple_pthread_getspecific_direct(unsigned long slot)
{
void *ret;
#if defined(__i386__) || defined(__x86_64__)
__asm__ ("mov %%gs:%1, %0" : "=r" (ret) : "m" (*(void**)(slot * sizeof(void *))));
#elif (defined(__arm__) && (defined(_ARM_ARCH_6) || defined(_ARM_ARCH_5)))
void **__pthread_tsd;
#if defined(__arm__) && defined(_ARM_ARCH_6)
uintptr_t __pthread_tpid;
__asm__ ("mrc p15, 0, %0, c13, c0, 3" : "=r" (__pthread_tpid));
__pthread_tsd = (void**)(__pthread_tpid & ~0x3ul);
#elif defined(__arm__) && defined(_ARM_ARCH_5)
register uintptr_t __pthread_tpid asm ("r9");
__pthread_tsd = (void**)__pthread_tpid;
#endif
ret = __pthread_tsd[slot];
#elif defined(__arm64__)
ret = pthread_getspecific(slot);
#else
#error no _pthread_getspecific_direct implementation for this arch
#endif
return ret;
}
inline static void apple_pthread_setspecific_direct(unsigned long slot, void * val)
{
#if defined(__i386__)
#if defined(__PIC__)
__asm__ ("movl %1,%%gs:%0" : "=m" (*(void**)(slot * sizeof(void *))) : "rn" (val));
#else
__asm__ ("movl %1,%%gs:%0" : "=m" (*(void**)(slot * sizeof(void *))) : "ri" (val));
#endif
#elif defined(__x86_64__)
/* PIC is free and cannot be disabled, even with: gcc -mdynamic-no-pic ... */
__asm__ ("movq %1,%%gs:%0" : "=m" (*(void**)(slot * sizeof(void *))) : "rn" (val));
#elif (defined(__arm__) && (defined(_ARM_ARCH_6) || defined(_ARM_ARCH_5)))
void **__pthread_tsd;
#if defined(__arm__) && defined(_ARM_ARCH_6)
uintptr_t __pthread_tpid;
__asm__ ("mrc p15, 0, %0, c13, c0, 3" : "=r" (__pthread_tpid));
__pthread_tsd = (void**)(__pthread_tpid & ~0x3ul);
#elif defined(__arm__) && defined(_ARM_ARCH_5)
register uintptr_t __pthread_tpid asm ("r9");
__pthread_tsd = (void**)__pthread_tpid;
#endif
__pthread_tsd[slot] = val;
#elif defined(__arm64__)
pthread_setspecific(slot, val);
#else
#error no _pthread_setspecific_direct implementation for this arch
#endif
}
#endif
pthread_key_t m_Key;
};
}
}
#endif