#include "CjScheduler.h"
#include <cstdlib>
#include <cstring>
#include <new>
#include <thread>
#include <vector>
#if defined(_WIN64)
#include <windows.h>
#elif defined(__APPLE__)
#include "sys/sysctl.h"
#else
#include "sys/sysinfo.h"
#endif
#include "Base/LogFile.h"
#include "Base/CString.h"
#include "Sync/Sync.h"
#include "Base/Panic.h"
#include "Cangjie.h"
#include "CangjieRuntime.h"
#include "Mutator/MutatorManager.h"
#include "RuntimeConfig.h"
#include "schedule.h"
#include "Concurrency/ConcurrencyModel.h"
#include "ExceptionManager.h"
#include "ExceptionManager.inline.h"
#include "Mutator/Mutator.inline.h"
#include "Interpreter/InterpreterSpecific.h"
#include "Interpreter/RuntimeAPI.h"
#include "securec.h"
#if defined(CANGJIE_SANITIZER_SUPPORT)
#include "Sanitizer/SanitizerInterface.h"
#endif
#include "CpuProfiler/CpuProfiler.h"
namespace MapleRuntime {
#ifdef __cplusplus
extern "C" {
#endif
static size_t g_initStackSize = 0;
static size_t g_sysmemSize = 1 * GB;
enum TimeUnit : uint32_t {
SECOND = 0,
MILLI_SECOND = 1,
MICRO_SECOND = 2,
NANO_SECOND = 3,
};
static void CheckSysmemSize()
{
#if defined(_WIN64)
MEMORYSTATUSEX memInfo;
memInfo.dwLength = sizeof(MEMORYSTATUSEX);
bool ret = GlobalMemoryStatusEx(&memInfo);
if (ret) {
g_sysmemSize = memInfo.ullTotalPhys;
} else {
LOG(RTLOG_ERROR, "Get system memory failed.\n");
}
#elif defined(__APPLE__)
const int sz = 2;
int mib[sz];
size_t length = sizeof(g_sysmemSize);
mib[0] = CTL_HW;
mib[1] = HW_MEMSIZE;
sysctl(mib, sz, &g_sysmemSize, &length, nullptr, 0);
#else
struct sysinfo memInfo;
int ret = sysinfo(&memInfo);
if (ret == 0) {
g_sysmemSize = memInfo.totalram * memInfo.mem_unit;
} else {
LOG(RTLOG_ERROR, "Get system memory failed. msg: %s.\n", strerror(errno));
}
#endif
}
* Init runtime's heap size from environment variable.
* The unit must be added when configuring "cjHeapSize", it supports "kb", "mb", "gb".
* Valid heap size range is [4MB, system memory size].
* for example:
* export cjHeapSize = 32GB
*/
static size_t InitHeapSize(size_t defaultParam)
{
auto env = std::getenv("cjHeapSize");
if (env == nullptr) {
return defaultParam;
}
size_t size = CString::ParseSizeFromEnv(env);
#if defined(__OHOS__) || defined(__ANDROID__)
size_t minSize = 64UL * KB;
#else
size_t minSize = 4UL * KB;
#endif
size_t maxSize = g_sysmemSize / KB;
if (size >= minSize && size <= maxSize) {
return size;
} else {
LOG(RTLOG_ERROR,
"Unsupported cjHeapSize parameter. The unit must be added when configuring with positive integer value, "
"it supports 'KB', 'MB', 'GB'. Valid cjHeapSize range is [%zuMB, system memory size].\n",
minSize / KB);
}
return defaultParam;
}
static size_t InitRegionSize(size_t defaultParam)
{
auto env = std::getenv("cjRegionSize");
if (env == nullptr) {
return defaultParam;
}
size_t size = CString::ParseSizeFromEnv(env);
size_t minSize = MapleRuntime::MRT_PAGE_SIZE / KB;
size_t maxSize = 2 * 1024UL;
if (size >= minSize && size <= maxSize) {
return size;
} else {
LOG(RTLOG_ERROR, "Unsupported cjRegionSize parameter. The unit must be added "
"when configuring with positive integer value. "
"Valid cjRegionSize range is [%zuKB, 2048KB].\n", minSize);
}
return defaultParam;
}
static double InitPercentParameterIncl(const char* name, double minSize, double maxSize, double defaultParam)
{
auto env = std::getenv(name);
if (env == nullptr) {
return defaultParam;
}
double size = CString::ParseValidFromEnv(env);
if (size - minSize >= 0 && maxSize - size >= 0) {
return size;
} else {
LOG(RTLOG_ERROR, "Unsupported %s parameter. The value must be a positive decimal. "
"Valid %s range is [%f, %f].\n", name, name, minSize, maxSize);
}
return defaultParam;
}
static double InitPercentParameter(const char* name, double minSize, double maxSize, double defaultParam)
{
auto env = std::getenv(name);
if (env != nullptr) {
double parameter = CString::ParsePosDecFromEnv(env);
if (parameter - minSize > 0 && maxSize - parameter >= 0) {
return parameter;
} else {
LOG(RTLOG_ERROR, "Unsupported %s parameter. The value must be a positive decimal. "
"Valid %s range is (%f, %f].\n", name, name, minSize, maxSize);
}
}
return defaultParam;
}
static size_t InitSizeParameter(const char* name, size_t minSize, size_t defaultParam)
{
auto env = std::getenv(name);
if (env != nullptr) {
size_t parameter = CString::ParseSizeFromEnv(env);
if (parameter > minSize) {
return parameter;
} else {
LOG(RTLOG_ERROR, "Unsupported %s parameter. The unit must be added "
"when configuring with positive integer value. "
"Valid %s range must be greater than %zuKB.\n",
name, name, minSize);
}
}
return defaultParam;
}
static uint64_t InitTimeParameter(const char* name, uint64_t minSize, uint64_t defaultParam)
{
auto env = std::getenv(name);
if (env != nullptr) {
uint64_t parameter = CString::ParseTimeFromEnv(env);
if (parameter > minSize) {
return parameter;
} else {
LOG(RTLOG_ERROR, "Unsupported %s parameter. The unit must be added "
"when configuring with positive integer value. "
"Valid %s range must be greater than %lluns.\n", name, name,
static_cast<unsigned long long>(minSize));
}
}
return defaultParam;
}
static double InitDecParameter(const char* name, double minSize, double defaultParam)
{
auto env = std::getenv(name);
if (env != nullptr) {
double parameter = CString::ParsePosDecFromEnv(env);
if (parameter - minSize > 0) {
return parameter;
} else {
LOG(RTLOG_ERROR, "Unsupported %s parameter. The value must be a positive decimal. "
"Valid %s range must be greater than %f.\n", name, name,
minSize);
}
}
return defaultParam;
}
* Init light-weight-thread stack size from environment variable.
* The unit must be added when configuring "cjStackSize", it supports "kb", "mb", "gb".
* Valid stack size range is [64KB, 1GB].
* for example:
* export cjStackSize = 100kb
*/
static size_t InitCoStackSize()
{
#ifdef _WIN64
size_t defaultStackSize = 128;
#elif defined(CANGJIE_SANITIZER_SUPPORT)
size_t defaultStackSize = 2048;
#elif defined(__OHOS__) || defined(__ANDROID__) || defined(__APPLE__)
size_t defaultStackSize = 1024;
#else
size_t defaultStackSize = 128;
#endif
size_t stackSize = 0;
auto env = std::getenv("cjStackSize");
if (env == nullptr) {
return defaultStackSize;
}
stackSize = CString::ParseSizeFromEnv(env);
#ifdef _WIN64
const size_t minStackSize = 128;
#else
const size_t minStackSize = 64;
#endif
const size_t maxStackSize = 1UL * MB;
if (stackSize >= minStackSize && stackSize <= maxStackSize) {
return stackSize;
} else {
LOG(RTLOG_ERROR,
"Unsupported cjStackSize parameter. The unit must be added when configuring with positive integer value, "
"it supports 'KB', 'MB', 'GB'. "
"Valid cjStackSize range is [128KB, 1GB] in Windows or [64KB, 1GB] in other systems.\n");
}
return defaultStackSize;
}
* Determine the max concurrency processors of cangjie program.
* If the environment variable `cjProcessorNum` is set, check whether it is in range (0, CPU_CORE * 2], use it if yes.
* Otherwise check whether the value of CPU_CORE is 0, use it if not.
* Otherwise use the default value 8.
*/
static uint32_t InitProcessorNum()
{
unsigned int cpus = std::thread::hardware_concurrency();
uint32_t defaultProcs = cpus != 0 ? static_cast<uint32_t>(cpus) : 8;
auto env = CString(std::getenv("cjProcessorNum"));
if (env.Str() == nullptr) {
return defaultProcs;
}
CString s = env.RemoveBlankSpace();
if (CString::IsPosNumber(s)) {
uint32_t custom = std::strtol(s.Str(), nullptr, 0);
const unsigned int maxMultiple = 2;
if (custom > 0 && custom <= (cpus * maxMultiple)) {
return custom;
} else {
LOG(RTLOG_ERROR,
"Unsupported cjProcessorNum parameter. The value must be a positive integer. "
"Valid cjProcessorNum range is (0, 2 * hardware_concurrency].\n");
}
}
return defaultProcs;
}
#ifdef INTERPRETER_ENABLED
static INT_InterpreterArgs InitInterpreterArgs(int& tokenCount)
{
tokenCount = 0;
const char* env = std::getenv("INTERPRETER_ARGS");
if (!env) {
return nullptr;
}
CString source(env);
std::vector<CString> tempTokens = CString::Split(source, ' ');
tokenCount = static_cast<int>(tempTokens.size());
if (tokenCount == 0) {
return nullptr;
}
INT_InterpreterArg* tokens = new (std::nothrow) INT_InterpreterArg[tokenCount + 1];
CHECK_DETAIL(tokens != nullptr, "new tokens array failed: size=%d", tokenCount + 1);
for (int i = 0; i < tokenCount; ++i) {
size_t tokenLen = tempTokens[i].Length();
char* token = new (std::nothrow) char[tokenLen + 1];
CHECK_DETAIL(token != nullptr, "new token #%d failed: size=%zu bytes", i, tokenLen + 1);
CHECK_DETAIL(strcpy_s(token, tokenLen + 1, tempTokens[i].Str()) == EOK, "strcpy_s failed");
tokens[i] = token;
}
tokens[tokenCount] = nullptr;
return tokens;
}
static const char* InitInterpreterLibName()
{
const char* env = std::getenv("INTERPRETER_LIB_NAME");
if (env == nullptr) {
return nullptr;
}
return strdup(env);
}
#endif
void* StartMainTask(void* arg, unsigned int len)
{
ScheduleHandle scheduler = MapleRuntime::Runtime::Current().GetConcurrencyModel().GetThreadScheduler();
ASSERT(arg != nullptr);
auto lwtData = static_cast<LWTData*>(arg);
auto execute = lwtData->execute;
Mutator* mutator = MutatorManager::Instance().CreateMutator();
uintptr_t threadData = MRT_GetThreadLocalData();
MRT_PreRunManagedCode(mutator, 0, reinterpret_cast<ThreadLocalData*>(threadData));
reinterpret_cast<ThreadLocalData*>(threadData)->isCJProcessor = true;
int32_t res = static_cast<int32_t>(
reinterpret_cast<uintptr_t>(ExecuteCangjieStub(0, 0, 0, execute, reinterpret_cast<void*>(threadData), 0)));
#ifdef CANGJIE_TSAN_SUPPORT
MapleRuntime::Sanitizer::TsanFinalize();
#endif
#ifdef __OHOS__
TRACE_FINISH_ASYNC(TRACE_CJTHREAD_EXEC, CJThreadId());
TRACE_START_ASYNC(TRACE_CJTHREAD_EXIT, CJThreadId());
#elif defined(__ANDROID__)
TRACE_FINISH();
TRACE_START(MapleRuntime::TraceInfoFormat(TRACE_CJTHREAD_EXIT, CJThreadId()));
#endif
CpuProfiler::GetInstance().TryStopSampling();
RTErrorCode rtCode = SetRuntimeFiniFlag();
if (rtCode != E_OK) {
LOG(RTLOG_FATAL, "Finish cj runtime failed for %d\n", rtCode);
}
MutatorManager::Instance().TransitMutatorToExit();
ScheduleStop(scheduler);
CangjieRuntime::FiniAndDelete();
#ifdef __OHOS__
TRACE_FINISH_ASYNC(TRACE_CJTHREAD_EXIT, CJThreadId());
#elif defined(__ANDROID__)
TRACE_FINISH();
#endif
constexpr uint8_t errRet = 255;
if (res > errRet || res < 0) {
res = -1;
}
exit(res);
}
#if defined(__APPLE__) && defined(__aarch64__)
using FutureFunc = UnitType (*)(void*, void*, void*);
#else
using FutureFunc = UnitType (*)(...);
#endif
static UnitType g_ut;
void* WrapperTask(void* arg, unsigned int len)
{
ASSERT(arg != nullptr);
auto lwtData = static_cast<LWTData*>(arg);
auto execute = lwtData->execute;
uintptr_t threadData = MRT_GetThreadLocalData();
Mutator* mutator = reinterpret_cast<ThreadLocalData*>(threadData)->mutator;
MRT_PreRunManagedCode(mutator, 0, reinterpret_cast<ThreadLocalData*>(threadData));
BaseObject* future = Heap::GetBarrier().ReadStaticRef(*(reinterpret_cast<RefField<>*>(&lwtData->obj)));
TypeInfo* typeInfo = future->GetTypeInfo();
#if defined(__aarch64__)
ExecuteCangjieStub(future, typeInfo, 0, execute, reinterpret_cast<void*>(threadData), &g_ut);
#elif defined(__arm__)
ExecuteCangjieStub(&g_ut, future, typeInfo, execute, threadData);
#elif defined(__x86_64__)
ExecuteCangjieStub(&g_ut, future, typeInfo, execute, threadData);
#endif
MutatorManager::Instance().TransitMutatorToExit();
return nullptr;
}
void* MCC_NewCJThread(void* execute, void* future, void* scheduler)
{
#if defined(CANGJIE_TSAN_SUPPORT)
void* pc = __builtin_return_address(0);
MapleRuntime::Sanitizer::TsanFuncEntry(pc);
#endif
LWTData data;
data.execute = execute;
data.threadObject = nullptr;
data.obj = nullptr;
RefField<>* runtimeRootField = reinterpret_cast<RefField<>*>(&data.obj);
Heap::GetBarrier().WriteStaticRef(*runtimeRootField, reinterpret_cast<BaseObject*>(future));
if (!scheduler) {
scheduler = MapleRuntime::Runtime::Current().GetConcurrencyModel().GetThreadScheduler();
}
CJThreadHandle handle = CJThreadNew(scheduler, nullptr, WrapperTask, &data, sizeof(LWTData));
if (handle == nullptr) {
#if defined(CANGJIE_TSAN_SUPPORT)
MapleRuntime::Sanitizer::TsanFuncExit();
#endif
return nullptr;
}
#if defined(CANGJIE_TSAN_SUPPORT)
MapleRuntime::Sanitizer::TsanFuncExit();
#endif
return handle;
}
bool MRT_NewForeignCJThread()
{
if (ThreadLocal::IsCJProcessor() || ThreadLocal::GetMutator() != nullptr) {
return false;
}
TRACE_START("CJRT_INVOKE_CJTASK");
ScheduleHandle scheduler = nullptr;
if (ThreadLocal::GetForeignCJThread() == nullptr) {
auto runtime = reinterpret_cast<MapleRuntime::CangjieRuntime*>(&MapleRuntime::Runtime::Current());
scheduler = runtime->CreateSubSchedulerAndInit(SCHEDULE_FOREIGN_THREAD);
CJThreadAttr attr;
CJThreadAttrInit(&attr);
CJThreadAttrNameSet(&attr, "cangjie");
LWTData data = {};
CJThreadHandle cjthread =
CJThreadNewToSchedule(scheduler, (const struct CJThreadAttr*)(&attr), WrapperTask, &data, sizeof(LWTData));
MutatorManager::Instance().SetMainThreadHandle(cjthread);
CJThreadPreemptOffCntAdd();
RebindCJThread(cjthread);
ThreadLocal::SetForeignCJThread(cjthread);
ThreadLocal::SetSchedule(scheduler);
ThreadLocal::SetProtectAddr(nullptr);
ScheduleNetpollInit();
}
void* cjthread = ThreadLocal::GetForeignCJThread();
ThreadLocal::SetCJThread(cjthread);
Mutator* mutator = reinterpret_cast<Mutator*>(CJThreadGetMutator());
MutatorManager::Instance().BindMutator(*mutator);
if (scheduler == nullptr) {
scheduler = GetCJThreadScheduler();
ThreadLocal::SetSchedule(scheduler);
ThreadLocal::SetProtectAddr(nullptr);
}
mutator->InitForeignCJThread();
SetSchedulerState(1);
#ifdef __OHOS__
TRACE_START_ASYNC(TRACE_CJTHREAD_EXEC, CJThreadGetId(cjthread));
#elif defined(__ANDROID__)
TRACE_START(MapleRuntime::TraceInfoFormat(TRACE_CJTHREAD_EXEC, CJThreadGetId(cjthread)));
#endif
return true;
}
bool MRT_EndForeignCJThread()
{
if (ThreadLocal::IsCJProcessor()) {
return false;
}
#if defined(__OHOS__)
unsigned long long cjthreadId = CJThreadGetId(ThreadLocal::GetForeignCJThread());
TRACE_FINISH_ASYNC(TRACE_CJTHREAD_EXEC, cjthreadId);
#elif defined(__ANDROID__)
TRACE_FINISH();
#endif
MapleRuntime::ExceptionManager::CheckAndDumpException();
MapleRuntime::Runtime::Current().GetMutatorManager().TransitMutatorToExit();
ThreadLocal::SetCJThread(nullptr);
SetSchedulerState(0);
TRACE_FINISH();
return true;
}
static void* WrapperExclusiveClosure(void* arg, unsigned int len)
{
ASSERT(arg != nullptr);
auto lwtData = static_cast<LWTData*>(arg);
uintptr_t threadData = MRT_GetThreadLocalData();
Mutator* mutator = reinterpret_cast<ThreadLocalData*>(threadData)->mutator;
MRT_PreRunManagedCode(mutator, 0, reinterpret_cast<ThreadLocalData*>(threadData));
lwtData->threadObject = nullptr;
BaseObject* executeClosure = Heap::GetBarrier().ReadStaticRef(reinterpret_cast<RefField<false>&>(lwtData->execute));
BaseObject* closureObj = Heap::GetBarrier().ReadStaticRef(reinterpret_cast<RefField<false>&>(lwtData->obj));
#if defined(__aarch64__)
ExecuteExclusiveCangjieStub(closureObj, nullptr, executeClosure, reinterpret_cast<void*>(threadData), &g_ut);
#elif defined(__x86_64__)
ExecuteExclusiveCangjieStub(&g_ut, nullptr, closureObj, executeClosure, reinterpret_cast<void*>(threadData));
#elif defined(__arm__)
TypeInfo* ti = closureObj->GetTypeInfo();
ExecuteExclusiveCangjieStub(&g_ut, ti, closureObj, executeClosure, reinterpret_cast<void*>(threadData));
#endif
MutatorManager::Instance().TransitMutatorToExit();
return nullptr;
}
void* MCC_NewExclusiveCJThread(void* executeClosure, void* closurePtr, void* futureTi)
{
LWTData data;
data.execute = nullptr;
data.obj = nullptr;
data.threadObject = futureTi;
RefField<>* executeRootField = reinterpret_cast<RefField<>*>(&data.obj);
Heap::GetBarrier().WriteStaticRef(*executeRootField, reinterpret_cast<BaseObject*>(closurePtr));
RefField<>* objRootField = reinterpret_cast<RefField<>*>(&data.execute);
Heap::GetBarrier().WriteStaticRef(*objRootField, reinterpret_cast<BaseObject*>(executeClosure));
return ExclusiveCJThreadNew(WrapperExclusiveClosure, &data, sizeof(LWTData));
}
static void ResetFinalizerThreadLocal()
{
ThreadLocal::SetCJThread(nullptr);
ThreadLocal::SetForeignCJThread(nullptr);
ThreadLocal::SetSchedule(nullptr);
ThreadLocal::SetAllocBuffer(nullptr);
ThreadLocal::SetMutator(nullptr);
ThreadLocal::SetProtectAddr(nullptr);
ThreadLocal::SetCJProcessorFlag(false);
ThreadLocal::SetThreadType(ThreadType::CJ_PROCESSOR);
}
static void FiniAndFreeFinalizerScheduler(ScheduleHandle scheduler)
{
if (scheduler == nullptr) {
return;
}
auto runtime = reinterpret_cast<MapleRuntime::CangjieRuntime*>(&MapleRuntime::Runtime::Current());
if (!runtime->FiniSubScheduler(scheduler)) {
LOG(RTLOG_ERROR, "failed to fini finalizer sub scheduler");
}
SetSchedulerState(5);
ScheduleNonDefaultFree(scheduler);
}
void* NewFinalizerCJThread()
{
ScheduleHandle scheduler = nullptr;
auto runtime = reinterpret_cast<MapleRuntime::CangjieRuntime*>(&MapleRuntime::Runtime::Current());
scheduler = runtime->CreateSubSchedulerAndInit(SCHEDULE_FOREIGN_THREAD);
if (scheduler == nullptr) {
LOG(RTLOG_ERROR, "failed to create finalizer scheduler");
return nullptr;
}
CJThreadAttr attr;
CJThreadAttrInit(&attr);
CJThreadAttrNameSet(&attr, "cangjie");
LWTData data = {};
CJThreadHandle cjthread = CJThreadNewToSchedule(scheduler, (const struct CJThreadAttr*)(&attr),
WrapperTask, &data, sizeof(LWTData),
CJTHREAD_CREATE_SOURCE_FINALIZER);
if (cjthread == nullptr) {
LOG(RTLOG_ERROR, "failed to create finalizer cjthread");
FiniAndFreeFinalizerScheduler(scheduler);
return nullptr;
}
ThreadLocal::SetThreadType(ThreadType::FP_THREAD);
ThreadLocal::SetCJProcessorFlag(true);
ThreadLocal::SetCJThread(cjthread);
CJThreadPreemptOffCntAdd();
RebindCJThread(cjthread);
ThreadLocal::SetForeignCJThread(cjthread);
ThreadLocal::SetSchedule(scheduler);
ThreadLocal::SetProtectAddr(reinterpret_cast<uint8_t*>(0));
ScheduleNetpollInit();
Mutator* mutator = reinterpret_cast<Mutator*>(CJThreadGetMutator());
CHECK_DETAIL(mutator != nullptr, "create is null when create finalizer cjthread");
MutatorManager::Instance().MutatorManagementRLock();
mutator->InitTid();
mutator->SetManagedContext(false);
MutatorManager::Instance().BindMutator(*mutator);
ThreadLocal::SetMutator(mutator);
MutatorManager::Instance().MutatorManagementRUnlock();
ThreadLocalData* threadData = reinterpret_cast<ThreadLocalData*>(MRT_GetThreadLocalData());
MRT_PreRunManagedCode(mutator, 2, threadData);
SetSchedulerState(1);
return cjthread;
}
void EndFinalizerCJThread()
{
auto* schedule = reinterpret_cast<ScheduleHandle>(ThreadLocal::GetSchedule());
Mutator* mutator = ThreadLocal::GetMutator();
CHECK_DETAIL(mutator != nullptr, "EndFinalizerCJThread with null mutator");
MutatorManager::Instance().MutatorManagementRLock();
(void)mutator->LeaveSaferegion();
mutator->ResetMutator();
MutatorManager::Instance().UnbindMutator(*mutator);
MutatorManager::Instance().MutatorManagementRUnlock();
FiniAndFreeFinalizerScheduler(schedule);
ResetFinalizerThreadLocal();
}
* The thread entry function provided by runtime.
* It is a wrapper function of the entry function from Cangjie frontend.
*/
static void* WrapperOfExecuteClosure(void* arg, unsigned int len)
{
ASSERT(arg != nullptr);
auto lwtData = static_cast<LWTData*>(arg);
auto executeClosure = lwtData->execute;
uintptr_t threadData = MRT_GetThreadLocalData();
Mutator* mutator = reinterpret_cast<ThreadLocalData*>(threadData)->mutator;
MRT_PreRunManagedCode(mutator, 0, reinterpret_cast<ThreadLocalData*>(threadData));
TypeInfo* futureTi = static_cast<TypeInfo*>(lwtData->threadObject);
lwtData->threadObject = nullptr;
BaseObject* closureObj = Heap::GetBarrier().ReadStaticRef(reinterpret_cast<RefField<false>&>(lwtData->obj));
#if defined(__aarch64__)
ExecuteCangjieStub(closureObj, futureTi, 0, executeClosure, reinterpret_cast<void*>(threadData), &g_ut);
#elif defined(__arm__)
ExecuteCangjieStub(&g_ut, closureObj, futureTi, executeClosure, threadData);
#elif defined(__x86_64__)
ExecuteCangjieStub(&g_ut,
closureObj,
futureTi,
executeClosure, threadData);
#endif
MutatorManager::Instance().TransitMutatorToExit();
return nullptr;
}
void* MCC_NewCJThreadNoReturn(void* executeClosure, void* closurePtr, void* scheduler, void* futureTi)
{
LWTData data;
data.execute = executeClosure;
data.obj = nullptr;
data.threadObject = futureTi;
RefField<>* runtimeRootField = reinterpret_cast<RefField<>*>(&data.obj);
Heap::GetBarrier().WriteStaticRef(*runtimeRootField, reinterpret_cast<BaseObject*>(closurePtr));
if (!scheduler) {
scheduler = MapleRuntime::Runtime::Current().GetConcurrencyModel().GetThreadScheduler();
}
CJThreadHandle handle =
CJThreadNewToSchedule(scheduler, nullptr, WrapperOfExecuteClosure, &data, sizeof(LWTData));
return handle;
}
* Determine the default stack size and heap size according to system memory.
* If system memory size is less then 1GB, heap size is 64MB and stack size is 64KB.
* Otherwise heap size is 256MB and stack size is 1MB.
*/
static RuntimeParam InitRuntimeParam()
{
CheckSysmemSize();
size_t initHeapSize = InitHeapSize(g_sysmemSize > 1 * GB ? 256 * KB : 64 * KB);
RuntimeParam param = {
.heapParam = {
#if defined(__OHOS__) || defined(__ANDROID__)
.regionSize = InitRegionSize(1024UL),
#else
.regionSize = InitRegionSize(64UL),
#endif
.heapSize = initHeapSize,
* The minimux live region threshold is 0% of region,
* the maximum is 100% of region, default to 80% of region.
*/
.exemptionThreshold = InitPercentParameter("cjExemptionThreshold", 0.0, 1.0, 0.8),
* The minimux heap utilization is 0% of heap,
* the maximum is 100% of heap, default to 80% of heap.
*/
.heapUtilization = InitPercentParameter("cjHeapUtilization", 0.0, 1.0, 0.8),
.heapGrowth = InitDecParameter("cjHeapGrowth", 0.0, 0.15),
.allocationRate = InitDecParameter("cjAllocationRate", 0.0, 10240),
.allocationWaitTime = static_cast<size_t>(InitTimeParameter("cjAllocationWaitTime", 0, 1000)),
},
.gcParam = {
.gcThreshold = InitSizeParameter("cjGCThreshold", 0, initHeapSize) * KB,
.garbageThreshold = InitPercentParameterIncl("cjGarbageThreshold", 0.0, 1.0, 0.5),
.gcInterval = InitTimeParameter("cjGCInterval", 0, 150 * MILLI_SECOND_TO_NANO_SECOND),
.backupGCInterval = InitTimeParameter("cjBackupGCInterval", 0, 240 * SECOND_TO_NANO_SECOND),
.gcThreads = 2,
},
.logParam = {
.logLevel = LogFile::GetLogLevel(),
},
.coParam = {
.thStackSize = 1 * KB,
.coStackSize = InitCoStackSize(),
.processorNum = InitProcessorNum(),
},
};
return param;
}
#ifdef INTERPRETER_ENABLED
static InterpreterParam InitInterpreterParam()
{
int interpreterArgsCount = 0;
INT_InterpreterArgs interpreterArgs = InitInterpreterArgs(interpreterArgsCount);
InterpreterParam interpreterParam = {
.interpreterLibName = InitInterpreterLibName(),
.interpreterArgsCount = interpreterArgsCount,
.interpreterArgs = interpreterArgs,
.appLibHandle = nullptr,
};
return interpreterParam;
}
static void DestroyInterpreterParam(InterpreterParam& interpreterParam)
{
INT_InterpreterArgs interpreterArgs = interpreterParam.interpreterArgs;
if (interpreterArgs != nullptr) {
for (int i = 0; i < interpreterParam.interpreterArgsCount && interpreterArgs[i] != nullptr; ++i) {
delete[] interpreterArgs[i];
}
delete[] interpreterArgs;
}
if (interpreterParam.interpreterLibName != nullptr) {
std::free(const_cast<char*>(interpreterParam.interpreterLibName));
}
interpreterParam.interpreterLibName = nullptr;
interpreterParam.interpreterArgsCount = 0;
interpreterParam.interpreterArgs = nullptr;
interpreterParam.appLibHandle = nullptr;
}
#endif
void MRT_CjRuntimeInit()
{
RuntimeParam param = InitRuntimeParam();
CangjieRuntime::CreateAndInit(param);
#ifdef INTERPRETER_ENABLED
InterpreterParam interpreterParam = InitInterpreterParam();
InitCJInterpreter(&interpreterParam);
DestroyInterpreterParam(interpreterParam);
#endif
RTErrorCode rtCode = SetRuntimeInitFlag();
if (rtCode != E_OK) {
LOG(RTLOG_FATAL, "Init cj runtime failed for %d\n", rtCode);
}
}
void MRT_SetCommandLineArgs(int argc, const char* argv[])
{
reinterpret_cast<CangjieRuntime*>(&MapleRuntime::Runtime::Current())->SetCommandLinesArgs(argc, argv);
return;
}
const char** MRT_GetCommandLineArgs()
{
return reinterpret_cast<CangjieRuntime*>(&MapleRuntime::Runtime::Current())->GetCommandLineArgs();
}
void MRT_CjRuntimeStart(void* execute)
{
ScheduleHandle scheduler = MapleRuntime::Runtime::Current().GetConcurrencyModel().GetThreadScheduler();
LWTData lwtData;
lwtData.execute = execute;
lwtData.fn = nullptr;
lwtData.obj = nullptr;
lwtData.threadObject = nullptr;
CJThreadAttr attr;
CJThreadAttrInit(&attr);
CJThreadAttrStackSizeSet(&attr, g_initStackSize * KB);
CJThreadAttrNameSet(&attr, "cangjie");
CJThreadHandle cjthread = CJThreadNew(scheduler, &attr, StartMainTask, &lwtData, sizeof(LWTData));
MutatorManager::Instance().SetMainThreadHandle(cjthread);
ScheduleStart();
}
int MRT_NewWaitQueue(void* waitQueuePtr) { return WaitqueueNew(reinterpret_cast<Waitqueue*>(waitQueuePtr)); }
bool ReportWaitQueueRetMsg(int err)
{
if (err == 0) {
return true;
}
if (err == WAIT_QUEUE_TIMEOUT) {
return false;
}
if (err == ERRNO_QUEUE_DOESNT_EXIST) {
LOG(RTLOG_INFO, "The waiting queue passed by the user does not exist!\n");
}
if (err == ERRNO_TIMER_STOP_FAILED) {
}
if (err == ERRNO_QUEUE_IS_EMPTY) {
}
if (err == ERRNO_CALLBACK_RETURN_TRUE) {
}
if (err == ERRNO_MALLOC_FAILED) {
LOG(RTLOG_INFO, "Memory allocation failed!\n");
}
if (err == ERRNO_TIMER_STATE_INVALID) {
LOG(RTLOG_INFO, "Illegal waiting time: time cannot be negative!\n");
}
return true;
}
void ReportSemRetMsg(int err)
{
if (err == 0) {
return;
}
if (err == ERRNO_SEMA_DOESNT_EXIST) {
LOG(RTLOG_ERROR, "The semaphore transferred by the user does not exist!\n");
}
}
bool MRT_SuspendWithTimeout(void* wq, WqCallbackFunc callBack, void* callBackObj, int64_t timeOut)
{
auto handle = reinterpret_cast<Waitqueue*>(wq);
return ReportWaitQueueRetMsg(WaitqueuePark(handle, timeOut, callBack, callBackObj, false));
}
void MRT_ResumeOne(void* wq, WqCallbackFunc callBack, void* callBackObj)
{
auto handle = reinterpret_cast<Waitqueue*>(wq);
ReportWaitQueueRetMsg(WaitqueueWakeOne(handle, callBack, callBackObj));
}
void MRT_ResumeAll(void* wq, WqCallbackFunc callBack, void* callBackObj)
{
auto handle = reinterpret_cast<Waitqueue*>(wq);
ReportWaitQueueRetMsg(WaitqueueWakeAll(handle, callBack, callBackObj));
}
int MRT_NewSem(void* semPtr) { return SemaNew(reinterpret_cast<Sema*>(semPtr), 0); }
void MRT_SemAcquire(void* sem, bool isPushToHead)
{
auto handle = reinterpret_cast<Sema*>(sem);
ReportSemRetMsg(SemaAcquire(handle, isPushToHead));
}
void MRT_SemRelease(void* sem)
{
auto handle = reinterpret_cast<Sema*>(sem);
ReportSemRetMsg(SemaRelease(handle));
}
int64_t MRT_GetCurrentThreadID() { return static_cast<int64_t>(CJThreadId()); }
struct SubSchedulerContextData {
ScheduleHandle schedule;
std::condition_variable* conditionVar;
std::atomic_bool* inited;
std::string threadName;
};
void* MRT_StartSubScheduler(void* args)
{
auto runtime = reinterpret_cast<CangjieRuntime*>(&MapleRuntime::Runtime::Current());
struct SubSchedulerContextData* data = static_cast<struct SubSchedulerContextData*>(args);
void* schedule = runtime->CreateSubSchedulerAndInit();
#ifdef __APPLE__
CHECK_PTHREAD_CALL(pthread_setname_np, (data->threadName.c_str()), "set sub-scheduler thread name");
#elif defined(__linux__) || defined(hongmeng)
CHECK_PTHREAD_CALL(prctl, (PR_SET_NAME, data->threadName.c_str()), "set sub-scheduler thread name");
#endif
data->schedule = schedule;
data->inited->store(true);
data->conditionVar->notify_all();
ThreadLocal::SetCJProcessorFlag(true);
ScheduleStart();
return nullptr;
}
int8_t MRT_StopSubScheduler(void* schedule)
{
auto runtime = reinterpret_cast<CangjieRuntime*>(&MapleRuntime::Runtime::Current());
if (!runtime->FiniSubScheduler(schedule)) {
LOG(RTLOG_ERROR, "Fail to stop sub-scheduler");
return 1;
}
return 0;
}
static void ResolveCycleRefImpl()
{
Heap::GetHeap().GetCollector().ResolveCycleRef();
}
extern "C" void CJ_MRT_RolveCycleRef()
{
RunResolveCycle(reinterpret_cast<void*>(&ResolveCycleRefImpl));
}
const void* MRT_RuntimeNewSubScheduler()
{
static int id = 0;
std::condition_variable conditionVariable;
std::atomic_bool subScheduleInited = ATOMIC_VAR_INIT(false);
std::mutex mtx;
struct SubSchedulerContextData args = {
.schedule = nullptr,
.conditionVar = &conditionVariable,
.inited = &subScheduleInited,
.threadName = ""
};
pthread_t thread;
pthread_attr_t attr;
size_t stackSize = CangjieRuntime::GetConcurrencyParam().thStackSize * MapleRuntime::KB;
std::string threadName = "sub-schedule" + std::to_string(id++);
args.threadName = threadName;
CHECK_PTHREAD_CALL(pthread_attr_init, (&attr), "init pthread attr");
CHECK_PTHREAD_CALL(pthread_attr_setdetachstate, (&attr, PTHREAD_CREATE_JOINABLE), "set pthread joinable");
CHECK_PTHREAD_CALL(pthread_attr_setstacksize, (&attr, stackSize), "set pthread stacksize");
CHECK_PTHREAD_CALL(pthread_create, (&thread, &attr, MRT_StartSubScheduler, &args), "create sub-scheduler thread");
#ifdef __WIN64
CHECK_PTHREAD_CALL(pthread_setname_np, (thread, threadName.c_str()), "set sub-scheduler thread name");
#endif
CHECK_PTHREAD_CALL(pthread_attr_destroy, (&attr), "destroy pthread attr");
std::unique_lock<std::mutex> lck(mtx);
conditionVariable.wait(lck, [&subScheduleInited]() { return subScheduleInited.load(); });
return args.schedule;
}
#ifdef __APPLE__
MRT_EXPORT void CJ_MRT_CjRuntimeInit();
__asm__(".global _CJ_MRT_CjRuntimeInit\n\t.set _CJ_MRT_CjRuntimeInit, _MRT_CjRuntimeInit");
MRT_EXPORT void CJ_MRT_CjRuntimeStart(void* execute);
__asm__(".global _CJ_MRT_CjRuntimeStart\n\t.set _CJ_MRT_CjRuntimeStart, _MRT_CjRuntimeStart");
MRT_EXPORT void* CJ_MCC_NewCJThread(void* execute, void* future, void* scheduler);
__asm__(
".global _CJ_MCC_NewCJThread\n\t.set _CJ_MCC_NewCJThread, _MCC_NewCJThread");
MRT_EXPORT void* CJ_MCC_NewCJThreadNoReturn(void* executeClosure, void* closurePtr, void* scheduler);
__asm__(
".global _CJ_MCC_NewCJThreadNoReturn\n\t.set _CJ_MCC_NewCJThreadNoReturn, "
"_MCC_NewCJThreadNoReturn");
MRT_EXPORT void CJ_MRT_SetCommandLineArgs(int argc, const char* argv[]);
__asm__(".global _CJ_MRT_SetCommandLineArgs\n\t.set _CJ_MRT_SetCommandLineArgs, _MRT_SetCommandLineArgs");
MRT_EXPORT const char** CJ_MRT_GetCommandLineArgs();
__asm__(".global _CJ_MRT_GetCommandLineArgs\n\t.set _CJ_MRT_GetCommandLineArgs, _MRT_GetCommandLineArgs");
MRT_EXPORT const void* CJ_MRT_RuntimeNewSubScheduler();
__asm__(".global _CJ_MRT_RuntimeNewSubScheduler\n\t.set _CJ_MRT_RuntimeNewSubScheduler, _MRT_RuntimeNewSubScheduler");
MRT_EXPORT int8_t CJ_MRT_StopSubScheduler(void* schedule);
__asm__(".global _CJ_MRT_StopSubScheduler\n\t.set _CJ_MRT_StopSubScheduler, _MRT_StopSubScheduler");
#else
MRT_EXPORT void CJ_MRT_CjRuntimeInit() __attribute__((alias("MRT_CjRuntimeInit")));
MRT_EXPORT void CJ_MRT_CjRuntimeStart(void* execute)
__attribute__((alias("MRT_CjRuntimeStart")));
MRT_EXPORT void* CJ_MCC_NewCJThread(void* execute, void* future, void *scheduler)
__attribute__((alias("MCC_NewCJThread")));
MRT_EXPORT void* CJ_MCC_NewCJThreadNoReturn(void* executeClosure, void* closurePtr, void* scheduler)
__attribute__((alias("MCC_NewCJThreadNoReturn")));
MRT_EXPORT void CJ_MRT_SetCommandLineArgs(int argc, const char* argv[])
__attribute__((alias("MRT_SetCommandLineArgs")));
MRT_EXPORT const char** CJ_MRT_GetCommandLineArgs() __attribute__((alias("MRT_GetCommandLineArgs")));
MRT_EXPORT const void* CJ_MRT_RuntimeNewSubScheduler() __attribute__((alias("MRT_RuntimeNewSubScheduler")));
MRT_EXPORT int8_t CJ_MRT_StopSubScheduler(void* schedule) __attribute__((alias("MRT_StopSubScheduler")));
#endif
#ifdef __cplusplus
};
#endif
}