r5sdk/r5dev/thirdparty/ea/EAThread/source/unix/eathread_stadia.cpp
Kawe Mazidjatari b3a68ed095 Add EABase, EAThread and DirtySDK to R5sdk
DirtySDK (EA's Dirty Sockets library) will be used for the LiveAPI implementation, and depends on: EABase, EAThread.
2024-04-05 18:29:03 +02:00

157 lines
3.3 KiB
C++

///////////////////////////////////////////////////////////////////////////////
// Copyright (c) Electronic Arts Inc. All rights reserved.
///////////////////////////////////////////////////////////////////////////////
#if defined(EA_PLATFORM_STADIA)
#include <stdlib.h>
#include <ctype.h>
#include <stdlib.h>
EATHREADLIB_API bool isUint(const char* buffer)
{
if (buffer == nullptr || *buffer == '\0')
return false;
while (isspace(*buffer) && *buffer != '\0')
++buffer;
return *buffer != '\0' && isdigit(*buffer);
}
EATHREADLIB_API EA::Thread::ThreadAffinityMask parseSet(char const* buffer, size_t len)
{
EA::Thread::ThreadAffinityMask mask = 0;
if (buffer == nullptr || len == 0)
return mask;
char const* ptr = buffer;
char const* end_ptr = buffer + len;
if (!isUint(ptr))
return mask;
uint32_t prevVal = strtoul(ptr, (char**)&ptr, 10);
uint32_t val = 0;
mask |= 1ULL << prevVal;
char op;
while (ptr < end_ptr)
{
op = *ptr;
++ptr;
switch (op)
{
case '-':
if (!isUint(ptr))
return mask;
val = strtoul(ptr, (char**)&ptr, 10);
for (int i = prevVal + 1; i <= val; ++i)
{
mask |= 1ULL << i;
}
break;
case ',':
if (!isUint(ptr))
return mask;
prevVal = strtoul(ptr, (char**)&ptr, 10);
mask |= 1ULL << prevVal;
break;
default:
EAT_ASSERT_MSG(isspace(op), "Invalid op token.");
break;
}
}
return mask;
}
EATHREADLIB_API bool readLine(const char* fileName, char* buffer, size_t len)
{
if (fileName == nullptr || buffer == nullptr || len == 0)
return false;
memset(buffer, 0, len);
FILE* fp = fopen(fileName, "r");
if (fp == nullptr)
return false;
bool ret = fgets(buffer, len, fp) != nullptr;
fclose(fp);
if (ret)
{
size_t slen = strlen(buffer);
if (buffer[slen - 1] == '\n')
buffer[slen - 1] = '\0';
}
return ret;
}
EATHREADLIB_API bool getProcessCpuSetCGroup(char* cgroup, size_t len)
{
return readLine("/proc/self/cpuset", cgroup, len);
}
EATHREADLIB_API EA::Thread::ThreadAffinityMask getInstanceCpuSet()
{
static const char* instanceFileName = "/sys/devices/system/cpu/present";
EA::Thread::ThreadAffinityMask mask = 0;
char buf[256];
if (!readLine(instanceFileName, buf, sizeof(buf)))
return mask;
return parseSet(buf, strlen(buf));
}
EATHREADLIB_API EA::Thread::ThreadAffinityMask initValidCpuAffinityMask()
{
char cgroup[32];
if (!getProcessCpuSetCGroup(cgroup, sizeof(cgroup)))
{
return getInstanceCpuSet();
}
char cpuSetFileName[256];
sprintf(cpuSetFileName, "/sys/fs/cgroup/cpuset%s/cpuset.cpus", cgroup);
char buf[256];
if (!readLine(cpuSetFileName, buf, sizeof(buf)))
return getInstanceCpuSet();
return parseSet(buf, strlen(buf));
}
EATHREADLIB_API EA::Thread::ThreadAffinityMask getValidCpuAffinityMask()
{
static EA::Thread::ThreadAffinityMask mask = initValidCpuAffinityMask();
return mask;
}
EATHREADLIB_API int initAvailableCpuCount()
{
EA::Thread::ThreadAffinityMask mask = getValidCpuAffinityMask();
int count = 1 & mask;
EAT_ASSERT_MSG(count, "First available cpu is not 0");
bool prevVal = count;
for (int i = 1; i < sizeof(mask); ++i)
{
bool val = (1 << i) & mask;
EAT_ASSERT_MSG(!val || prevVal, "Non-contiguous range of available cpus");
count += val;
prevVal = val;
}
return count;
}
EATHREADLIB_API int getAvailableCpuCount()
{
static int count = initAvailableCpuCount();
return count;
}
#endif