#include "../Platform/stdafx.h" #include "C4JThread.h" #ifdef __PSVITA__ #include "../../Minecraft.Client/Platform/PSVita/PSVitaExtras/ShutdownManager.h" #include "../../Minecraft.Client/Platform/PSVita/PSVitaExtras/PSVitaTLSStorage.h" // AP - this comes from the low level user_malloc.c file used to overide the // default memory functions. These must be called when a thread is // started/stopped extern "C" { extern void user_registerthread(); extern void user_removethread(); } #else #include "../../Minecraft.Client/Platform/PS3/PS3Extras/ShutdownManager.h" #endif std::vector C4JThread::ms_threadList; CRITICAL_SECTION C4JThread::ms_threadListCS; #ifdef _XBOX_ONE // 4J Stu - On XboxOne the main thread is not the one that does all the static // init, so we have to set this up later C4JThread* C4JThread::m_mainThread = NULL; void C4JThread::StaticInit() { m_mainThread = new C4JThread("Main thread"); } #else C4JThread C4JThread::m_mainThread("Main thread"); #endif #ifdef __ORBIS__ __thread SceKernelCpumask C4JThread::m_oldAffinityMask; #endif #if __PSVITA__ static SceInt32 g_DefaultCPU; static SceInt32 g_DefaultPriority; #endif C4JThread::C4JThread(C4JThreadStartFunc* startFunc, void* param, const char* threadName, int stackSize /* = 0*/) { m_startFunc = startFunc; m_threadParam = param; m_stackSize = stackSize; // to match XBox, if the stack size is zero, use the default 64k if (m_stackSize == 0) m_stackSize = 65536 * 2; // make sure it's at least 16K if (m_stackSize < 16384) m_stackSize = 16384; #ifdef __PS3__ sprintf(m_threadName, "(4J) %s", threadName); #else sprintf_s(m_threadName, 64, "(4J) %s", threadName); #endif m_isRunning = false; m_hasStarted = false; m_exitCode = STILL_ACTIVE; #ifdef __PS3__ m_completionFlag = new Event(Event::e_modeManualClear); m_threadID = 0; m_lastSleepTime = 0; m_priority = 1002; // main thread has priority 1001 #elif defined __ORBIS__ m_completionFlag = new Event(Event::e_modeManualClear); m_threadID = 0; m_lastSleepTime = 0; scePthreadAttrInit(&m_threadAttr); int err = scePthreadAttrSetaffinity( &m_threadAttr, 63); // set the thread affinity to all cores to start with assert(err == SCE_OK); m_oldAffinityMask = 0; m_priority = SCE_KERNEL_PRIO_FIFO_DEFAULT; #elif defined __PSVITA__ m_completionFlag = new Event(Event::e_modeManualClear); m_threadID = 0; m_lastSleepTime = 0; m_priority = g_DefaultPriority; // m_CPUMask = SCE_KERNEL_CPU_MASK_USER_ALL; // AP - I had trouble getting the cpu to change once the thread was created // so I've hard coded them here The main work division is... 0 - Main 1 - // Chunk/Tile Update 2 - Server/Audio These three can sometimes consume ALL // the CPU time so they are set to below average priority so as not to block // other critical threads int CPU = SCE_KERNEL_CPU_MASK_USER_ALL; if (!strcmp(threadName, "Chunk update")) { CPU = SCE_KERNEL_CPU_MASK_USER_2; m_priority = g_DefaultPriority + 1; } if (!strcmp(threadName, "Server")) { CPU = SCE_KERNEL_CPU_MASK_USER_1; m_priority = g_DefaultPriority + 1; } // make sure Tile Update doesn't go on cpu 0 because it will hold up the // main thread. And it can't go on cpu 1 because Chunk Update crashes. if (!strcmp(threadName, "Tile update")) { CPU = SCE_KERNEL_CPU_MASK_USER_1; } m_threadID = sceKernelCreateThread( m_threadName, entryPoint, g_DefaultPriority, m_stackSize, 0, CPU, NULL); app.DebugPrintf( "***************************** start thread %s " "**************************\n", m_threadName); #else m_threadID = 0; m_threadHandle = 0; DWORD threadID = 0; m_threadHandle = CreateThread(NULL, m_stackSize, entryPoint, this, CREATE_SUSPENDED, &threadID); m_threadID = threadID; #endif EnterCriticalSection(&ms_threadListCS); ms_threadList.push_back(this); LeaveCriticalSection(&ms_threadListCS); } // only used for the main thread C4JThread::C4JThread(const char* mainThreadName) { #ifdef __PSVITA__ user_registerthread(); #endif m_startFunc = NULL; m_threadParam = NULL; m_stackSize = 0; #ifdef __PS3__ sprintf(m_threadName, "(4J) %s", mainThreadName); #else sprintf_s(m_threadName, 64, "(4J) %s", mainThreadName); #endif m_isRunning = true; m_hasStarted = true; m_lastSleepTime = System::currentTimeMillis(); // should be the first thread to be created, so init the static critical // section for the threadlist here InitializeCriticalSection(&ms_threadListCS); #ifdef __PS3__ m_completionFlag = new Event(Event::e_modeManualClear); sys_ppu_thread_get_id(&m_threadID); #elif defined __ORBIS__ m_completionFlag = new Event(Event::e_modeManualClear); m_threadID = scePthreadSelf(); m_priority = SCE_KERNEL_PRIO_FIFO_DEFAULT; #elif defined __PSVITA__ m_completionFlag = new Event(Event::e_modeManualClear); g_DefaultPriority = sceKernelGetThreadCurrentPriority(); m_threadID = sceKernelGetThreadId(); int err = sceKernelChangeThreadCpuAffinityMask(m_threadID, SCE_KERNEL_CPU_MASK_USER_0); // sceKernelChangeThreadPriority(m_threadID, g_DefaultPriority + 1); g_DefaultCPU = SCE_KERNEL_CPU_MASK_USER_ALL; // sceKernelGetThreadCpuAffinityMask(m_threadID); #else m_threadID = GetCurrentThreadId(); m_threadHandle = GetCurrentThread(); #endif #ifdef _XBOX_ONE SetThreadName(-1, m_threadName); #endif EnterCriticalSection(&ms_threadListCS); ms_threadList.push_back(this); LeaveCriticalSection(&ms_threadListCS); } C4JThread::~C4JThread() { #if defined __PS3__ || defined __ORBIS__ || defined __PSVITA__ delete m_completionFlag; #endif #if defined __ORBIS__ scePthreadJoin(m_threadID, NULL); #endif EnterCriticalSection(&ms_threadListCS); for (AUTO_VAR(it, ms_threadList.begin()); it != ms_threadList.end(); it++) { if ((*it) == this) { ms_threadList.erase(it); LeaveCriticalSection(&ms_threadListCS); return; } } LeaveCriticalSection(&ms_threadListCS); } #ifdef __PS3__ void C4JThread::entryPoint(uint64_t param) { C4JThread* pThread = (C4JThread*)param; pThread->m_exitCode = (*pThread->m_startFunc)(pThread->m_threadParam); pThread->m_completionFlag->Set(); pThread->m_isRunning = false; sys_ppu_thread_exit(0); } #elif defined __ORBIS__ void* C4JThread::entryPoint(void* param) { C4JThread* pThread = (C4JThread*)param; pThread->m_exitCode = (*pThread->m_startFunc)(pThread->m_threadParam); pThread->m_completionFlag->Set(); pThread->m_isRunning = false; scePthreadExit(NULL); } #elif defined __PSVITA__ struct StrArg { C4JThread* Thread; }; SceInt32 C4JThread::entryPoint(SceSize argSize, void* pArgBlock) { StrArg* strArg = (StrArg*)pArgBlock; C4JThread* pThread = strArg->Thread; user_registerthread(); pThread->m_exitCode = (*pThread->m_startFunc)(pThread->m_threadParam); app.DebugPrintf( "***************************** thread exit %s " "**************************\n", pThread->m_threadName); pThread->m_completionFlag->Set(); pThread->m_isRunning = false; // AP - make sure we clean up this thread's storage and memory PSVitaTLSStorage::RemoveThread(pThread->m_threadID); user_removethread(); sceKernelExitDeleteThread(NULL); return pThread->m_exitCode; } #else DWORD WINAPI C4JThread::entryPoint(LPVOID lpParam) { C4JThread* pThread = (C4JThread*)lpParam; SetThreadName(-1, pThread->m_threadName); pThread->m_exitCode = (*pThread->m_startFunc)(pThread->m_threadParam); pThread->m_isRunning = false; return pThread->m_exitCode; } #endif void C4JThread::Run() { #ifdef __PS3__ // prio specifies the priority value of the PPU thread within the // range from 0 to 3071 where 0 is the highest. // One of the following values is set to flags: // 0 - non-joinable non-interrupt thread // SYS_PPU_THREAD_CREATE_JOINABLE - Create a joinable thread // SYS_PPU_THREAD_CREATE_INTERRUPT - Create an interrupt thread uint64_t flags = 0; int err = sys_ppu_thread_create(&m_threadID, entryPoint, (uint64_t)this, m_priority, m_stackSize, flags, m_threadName); #elif defined __ORBIS__ scePthreadAttrSetstacksize(&m_threadAttr, m_stackSize); scePthreadAttrSetguardsize(&m_threadAttr, 1024); int ret = scePthreadCreate(&m_threadID, &m_threadAttr, entryPoint, this, m_threadName); assert(ret == SCE_OK); scePthreadSetprio(m_threadID, m_priority); scePthreadAttrDestroy(&m_threadAttr); #elif defined __PSVITA__ StrArg strArg = {this}; // m_threadID = sceKernelCreateThread(m_threadName, entryPoint, m_priority, // m_stackSize, 0, m_CPUMask, NULL); sceKernelStartThread(m_threadID, sizeof(strArg), &strArg); #else ResumeThread(m_threadHandle); #endif m_lastSleepTime = System::currentTimeMillis(); m_isRunning = true; m_hasStarted = true; } void C4JThread::SetProcessor(int proc) { #ifdef __PS3__ // does nothing since we only have the 1 processor #elif defined __ORBIS__ scePthreadAttrSetaffinity(&m_threadAttr, 1 << proc); #elif defined __PSVITA__ int Proc = proc >> 1; // convert from 360's 3 cores * 2 hardware threads to Vita's 3 cores int Mask = SCE_KERNEL_CPU_MASK_USER_0 << Proc; // m_CPUMask = Mask; // int err = sceKernelChangeThreadCpuAffinityMask(m_threadID, Mask); int Newmask = sceKernelGetThreadCpuAffinityMask(m_threadID); app.DebugPrintf( "***************************** set thread proc %s %d %d %d " "**************************\n", m_threadName, proc, Mask, Newmask); #elif defined _DURANGO SetThreadAffinityMask(m_threadHandle, 1 << proc); #else XSetThreadProcessor(m_threadHandle, proc); #endif } void C4JThread::SetPriority(int priority) { #ifdef __PS3__ switch (priority) { case THREAD_PRIORITY_LOWEST: m_priority = 1003; break; case THREAD_PRIORITY_BELOW_NORMAL: m_priority = 1002; break; case THREAD_PRIORITY_NORMAL: m_priority = 1001; break; // same as main thread case THREAD_PRIORITY_ABOVE_NORMAL: m_priority = 1000; break; case THREAD_PRIORITY_HIGHEST: m_priority = 999; break; } if (m_threadID != 0) sys_ppu_thread_set_priority(m_threadID, m_priority); // int erro = sys_ppu_thread_set_priority(m_threadID, priority); #elif defined __ORBIS__ switch (priority) { case THREAD_PRIORITY_LOWEST: m_priority = SCE_KERNEL_PRIO_FIFO_LOWEST; break; case THREAD_PRIORITY_BELOW_NORMAL: m_priority = SCE_KERNEL_PRIO_FIFO_LOWEST + ((SCE_KERNEL_PRIO_FIFO_DEFAULT - SCE_KERNEL_PRIO_FIFO_LOWEST) / 2); break; case THREAD_PRIORITY_NORMAL: m_priority = SCE_KERNEL_PRIO_FIFO_DEFAULT; break; // same as main thread case THREAD_PRIORITY_ABOVE_NORMAL: m_priority = SCE_KERNEL_PRIO_FIFO_DEFAULT + ((SCE_KERNEL_PRIO_FIFO_HIGHEST - SCE_KERNEL_PRIO_FIFO_DEFAULT) / 2); break; case THREAD_PRIORITY_HIGHEST: m_priority = SCE_KERNEL_PRIO_FIFO_HIGHEST; break; } if (m_threadID != 0) { scePthreadSetprio(m_threadID, m_priority); } #elif defined __PSVITA__ int Mid = g_DefaultPriority; //(SCE_KERNEL_LOWEST_PRIORITY_USER + // SCE_KERNEL_HIGHEST_PRIORITY_USER) / 2; switch (priority) { case THREAD_PRIORITY_LOWEST: m_priority = SCE_KERNEL_LOWEST_PRIORITY_USER; break; case THREAD_PRIORITY_BELOW_NORMAL: m_priority = Mid + 1; break; case THREAD_PRIORITY_NORMAL: m_priority = Mid; break; // same as main thread case THREAD_PRIORITY_ABOVE_NORMAL: m_priority = Mid - 1; break; case THREAD_PRIORITY_HIGHEST: m_priority = SCE_KERNEL_HIGHEST_PRIORITY_USER; break; } // sceKernelChangeThreadPriority(m_threadID, m_priority); app.DebugPrintf( "***************************** set thread prio %s %d %d " "**************************\n", m_threadName, priority, m_priority); #else SetThreadPriority(m_threadHandle, priority); #endif // __PS3__ } std::uint32_t C4JThread::WaitForCompletion(int timeoutMs) { #ifdef __PS3__ if (timeoutMs == INFINITE) timeoutMs = SYS_NO_TIMEOUT; return m_completionFlag->WaitForSignal(timeoutMs); #elif defined __ORBIS__ return m_completionFlag->WaitForSignal(timeoutMs); #elif defined __PSVITA__ return m_completionFlag->WaitForSignal(timeoutMs); /* SceUInt32 Timeout = timeoutMs * 1000; SceInt32 err = sceKernelWaitThreadEnd(m_threadID, &m_exitCode, &Timeout); if( err == 0 ) { return m_exitCode; } else { if( err == SCE_KERNEL_ERROR_WAIT_TIMEOUT ) { return WAIT_TIMEOUT; } else { // AP - not sure what to do here return 0; } }*/ // return m_exitCode; #else return WaitForSingleObject(m_threadHandle, timeoutMs); #endif // __PS3__ } int C4JThread::GetExitCode() { #if defined __PS3__ || defined __ORBIS__ || defined __PSVITA__ return m_exitCode; #else DWORD exitcode = 0; GetExitCodeThread(m_threadHandle, &exitcode); return *((int*)&exitcode); #endif } void C4JThread::Sleep(int millisecs) { #ifdef __PS3__ if (millisecs == 0) { // https://ps3.scedev.net/forums/thread/116470/ // "sys_timer_usleep(0) does not yield the CPU." sys_ppu_thread_yield(); } else sys_timer_usleep(millisecs * 1000); #elif defined __ORBIS__ sceKernelUsleep(((SceKernelUseconds)millisecs) * 1000); #elif defined __PSVITA__ // 4J Stu - 0 is an error, so add a tiny sleep when we just want to yield sceKernelDelayThread(millisecs * 1000 + 1); #else ::Sleep(millisecs); #endif // __PS3__ } C4JThread* C4JThread::getCurrentThread() { #ifdef __PS3__ sys_ppu_thread_t currThreadID; sys_ppu_thread_get_id(&currThreadID); #elif defined __ORBIS__ ScePthread currThreadID = scePthreadSelf(); #elif defined __PSVITA__ SceUID currThreadID = sceKernelGetThreadId(); #else std::uint32_t currThreadID = GetCurrentThreadId(); #endif //__PS3__ EnterCriticalSection(&ms_threadListCS); for (int i = 0; i < ms_threadList.size(); i++) { if (currThreadID == ms_threadList[i]->m_threadID) { LeaveCriticalSection(&ms_threadListCS); return ms_threadList[i]; } } LeaveCriticalSection(&ms_threadListCS); return NULL; } bool C4JThread::isMainThread() { #ifdef _XBOX_ONE return getCurrentThread() == m_mainThread; #else return getCurrentThread() == &m_mainThread; #endif } C4JThread::Event::Event(EMode mode /* = e_modeAutoClear*/) { m_mode = mode; #ifdef __PS3__ sys_event_flag_attribute_t attr; // default values taken from sys_event_flag_attribute_initialize attr.attr_protocol = SYS_SYNC_PRIORITY; attr.attr_pshared = SYS_SYNC_NOT_PROCESS_SHARED; attr.key = 0; attr.flags = 0; attr.type = SYS_SYNC_WAITER_SINGLE; attr.name[0] = '\0'; sys_event_flag_attribute_initialize(attr); int err = sys_event_flag_create(&m_event, &attr, 0); #elif defined __ORBIS__ char name[1] = {0}; sceKernelCreateEventFlag( &m_event, name, SCE_KERNEL_EVF_ATTR_TH_FIFO | SCE_KERNEL_EVF_ATTR_MULTI, 0, NULL); #elif defined __PSVITA__ char name[1] = {0}; m_event = sceKernelCreateEventFlag( name, SCE_KERNEL_EVF_ATTR_TH_FIFO | SCE_KERNEL_EVF_ATTR_MULTI, 0, NULL); #else m_event = CreateEvent(NULL, (m_mode == e_modeManualClear), FALSE, NULL); #endif //__PS3__ } C4JThread::Event::~Event() { #ifdef __PS3__ sys_event_flag_destroy(m_event); #elif defined __ORBIS__ sceKernelDeleteEventFlag(m_event); #elif defined __PSVITA__ sceKernelDeleteEventFlag(m_event); #else CloseHandle(m_event); #endif // __PS3__ } void C4JThread::Event::Set() { #ifdef __PS3__ int err = sys_event_flag_set(m_event, 1); #elif defined __ORBIS__ sceKernelSetEventFlag(m_event, 1); #elif defined __PSVITA__ sceKernelSetEventFlag(m_event, 1); #else SetEvent(m_event); #endif //__PS3__ } void C4JThread::Event::Clear() { #ifdef __PS3__ int err = sys_event_flag_clear(m_event, ~(1)); #elif defined __ORBIS__ sceKernelClearEventFlag(m_event, ~(1)); #elif defined __PSVITA__ sceKernelClearEventFlag(m_event, ~1); #else ResetEvent(m_event); #endif //__PS3__ } std::uint32_t C4JThread::Event::WaitForSignal(int timeoutMs) { #ifdef __PS3__ if (timeoutMs == INFINITE) timeoutMs = SYS_NO_TIMEOUT; int timoutMicrosecs = timeoutMs * 1000; uint32_t mode = SYS_EVENT_FLAG_WAIT_AND; if (m_mode == e_modeAutoClear) mode |= SYS_EVENT_FLAG_WAIT_CLEAR; int err = sys_event_flag_wait(m_event, 1, mode, 0, timoutMicrosecs); switch (err) { case CELL_OK: return WAIT_OBJECT_0; case ETIMEDOUT: return WAIT_TIMEOUT; case ECANCELED: return WAIT_ABANDONED; default: return WAIT_FAILED; } #elif defined __ORBIS__ SceKernelUseconds timeoutMicrosecs; SceKernelUseconds* pTimeoutMicrosecs; if (timeoutMs == INFINITE) { pTimeoutMicrosecs = NULL; } else { timeoutMicrosecs = ((SceKernelUseconds)timeoutMs) * 1000; pTimeoutMicrosecs = &timeoutMicrosecs; } uint32_t waitMode = SCE_KERNEL_EVF_WAITMODE_AND; if (m_mode == e_modeAutoClear) { waitMode |= SCE_KERNEL_EVF_WAITMODE_CLEAR_PAT; } int err = sceKernelWaitEventFlag(m_event, 1, waitMode, NULL, pTimeoutMicrosecs); switch (err) { case SCE_OK: return WAIT_OBJECT_0; case SCE_KERNEL_ERROR_ETIMEDOUT: return WAIT_TIMEOUT; case SCE_KERNEL_ERROR_ECANCELED: return WAIT_ABANDONED; default: return WAIT_FAILED; } #elif defined __PSVITA__ SceUInt32 timeoutMicrosecs; SceUInt32* pTimeoutMicrosecs; if (timeoutMs == INFINITE) { pTimeoutMicrosecs = NULL; } else { timeoutMicrosecs = ((SceInt32)timeoutMs) * 1000; pTimeoutMicrosecs = &timeoutMicrosecs; } uint32_t waitMode = SCE_KERNEL_EVF_WAITMODE_AND; if (m_mode == e_modeAutoClear) { waitMode |= SCE_KERNEL_EVF_WAITMODE_CLEAR_ALL; } int err = sceKernelWaitEventFlag(m_event, 1, waitMode, NULL, pTimeoutMicrosecs); switch (err) { case SCE_OK: return WAIT_OBJECT_0; case SCE_KERNEL_ERROR_WAIT_TIMEOUT: return WAIT_TIMEOUT; case SCE_KERNEL_ERROR_WAIT_CANCEL: return WAIT_ABANDONED; default: return WAIT_FAILED; } #else return WaitForSingleObject(m_event, timeoutMs); #endif // __PS3__ } C4JThread::EventArray::EventArray(int size, EMode mode /* = e_modeAutoClear*/) { assert(size < 32); m_size = size; m_mode = mode; #ifdef __PS3__ sys_event_flag_attribute_t attr; // default values taken from sys_event_flag_attribute_initialize attr.attr_protocol = SYS_SYNC_PRIORITY; attr.attr_pshared = SYS_SYNC_NOT_PROCESS_SHARED; attr.key = 0; attr.flags = 0; attr.type = SYS_SYNC_WAITER_SINGLE; attr.name[0] = '\0'; sys_event_flag_attribute_initialize(attr); int err = sys_event_flag_create(&m_events, &attr, 0); assert(err == CELL_OK); #elif defined __ORBIS__ char name[1] = {0}; sceKernelCreateEventFlag( &m_events, name, SCE_KERNEL_EVF_ATTR_TH_FIFO | SCE_KERNEL_EVF_ATTR_MULTI, 0, NULL); #elif defined __PSVITA__ char name[1] = {0}; m_events = sceKernelCreateEventFlag( name, SCE_KERNEL_EVF_ATTR_TH_FIFO | SCE_KERNEL_EVF_ATTR_MULTI, 0, NULL); #else m_events = new HANDLE[size]; for (int i = 0; i < size; i++) { m_events[i] = CreateEvent(NULL, (m_mode == e_modeManualClear), FALSE, NULL); } #endif // __PS3__ } void C4JThread::EventArray::Set(int index) { #ifdef __PS3__ int err = sys_event_flag_set(m_events, 1 << index); assert(err == CELL_OK); #elif defined __ORBIS__ sceKernelSetEventFlag(m_events, 1 << index); #elif defined __PSVITA__ sceKernelSetEventFlag(m_events, 1 << index); #else SetEvent(m_events[index]); #endif //__PS3__ } void C4JThread::EventArray::Clear(int index) { #ifdef __PS3__ int err = sys_event_flag_clear(m_events, ~(1 << index)); assert(err == CELL_OK); #elif defined __ORBIS__ sceKernelClearEventFlag(m_events, ~(1 << index)); #elif defined __PSVITA__ sceKernelClearEventFlag(m_events, ~(1 << index)); #else ResetEvent(m_events[index]); #endif //__PS3__ } void C4JThread::EventArray::SetAll() { for (int i = 0; i < m_size; i++) Set(i); } void C4JThread::EventArray::ClearAll() { for (int i = 0; i < m_size; i++) Clear(i); } std::uint32_t C4JThread::EventArray::WaitForSingle(int index, int timeoutMs) { std::uint32_t retVal; #ifdef __PS3__ int timeoutMicrosecs; if (timeoutMs == INFINITE) timeoutMicrosecs = SYS_NO_TIMEOUT; else timeoutMicrosecs = timeoutMs * 1000; uint32_t mode = SYS_EVENT_FLAG_WAIT_AND; if (m_mode == e_modeAutoClear) mode |= SYS_EVENT_FLAG_WAIT_CLEAR; int err = sys_event_flag_wait(m_events, 1 << index, mode, 0, timeoutMicrosecs); switch (err) { case CELL_OK: retVal = WAIT_OBJECT_0; break; case ETIMEDOUT: retVal = WAIT_TIMEOUT; break; case ECANCELED: retVal = WAIT_ABANDONED; break; default: assert(0); retVal = WAIT_FAILED; break; } #elif defined __ORBIS__ SceKernelUseconds timeoutMicrosecs; SceKernelUseconds* pTimeoutMicrosecs; if (timeoutMs == INFINITE) { pTimeoutMicrosecs = NULL; } else { timeoutMicrosecs = ((SceKernelUseconds)timeoutMs) * 1000; pTimeoutMicrosecs = &timeoutMicrosecs; } uint32_t waitMode = SCE_KERNEL_EVF_WAITMODE_AND; if (m_mode == e_modeAutoClear) { waitMode |= SCE_KERNEL_EVF_WAITMODE_CLEAR_PAT; } uint64_t resultPat; int err = sceKernelWaitEventFlag(m_events, 1 << index, waitMode, &resultPat, pTimeoutMicrosecs); assert(err != SCE_KERNEL_ERROR_ETIMEDOUT); switch (err) { case SCE_OK: retVal = WAIT_OBJECT_0; break; case SCE_KERNEL_ERROR_ETIMEDOUT: retVal = WAIT_TIMEOUT; break; case SCE_KERNEL_ERROR_ECANCELED: retVal = WAIT_ABANDONED; break; default: retVal = WAIT_FAILED; break; } #elif defined __PSVITA__ SceUInt32 timeoutMicrosecs; SceUInt32* pTimeoutMicrosecs; if (timeoutMs == INFINITE) { pTimeoutMicrosecs = NULL; } else { timeoutMicrosecs = ((SceUInt32)timeoutMs) * 1000; pTimeoutMicrosecs = &timeoutMicrosecs; } uint32_t waitMode = SCE_KERNEL_EVF_WAITMODE_AND; if (m_mode == e_modeAutoClear) { waitMode |= SCE_KERNEL_EVF_WAITMODE_CLEAR_ALL; } int err = sceKernelWaitEventFlag(m_events, 1 << index, waitMode, NULL, pTimeoutMicrosecs); switch (err) { case SCE_OK: return WAIT_OBJECT_0; case SCE_KERNEL_ERROR_WAIT_TIMEOUT: return WAIT_TIMEOUT; case SCE_KERNEL_ERROR_WAIT_CANCEL: return WAIT_ABANDONED; default: return WAIT_FAILED; } #else retVal = WaitForSingleObject(m_events[index], timeoutMs); #endif // __PS3__ return retVal; } std::uint32_t C4JThread::EventArray::WaitForAll(int timeoutMs) { std::uint32_t retVal; #ifdef __PS3__ if (timeoutMs == INFINITE) timeoutMs = SYS_NO_TIMEOUT; int timoutMicrosecs = timeoutMs * 1000; unsigned int bitmask = 0; for (int i = 0; i < m_size; i++) bitmask |= (1 << i); uint32_t mode = SYS_EVENT_FLAG_WAIT_AND; if (m_mode == e_modeAutoClear) mode |= SYS_EVENT_FLAG_WAIT_CLEAR; int err = sys_event_flag_wait(m_events, bitmask, mode, 0, timoutMicrosecs); switch (err) { case CELL_OK: retVal = WAIT_OBJECT_0; break; case ETIMEDOUT: retVal = WAIT_TIMEOUT; break; case ECANCELED: retVal = WAIT_ABANDONED; break; default: assert(0); retVal = WAIT_FAILED; break; } #elif defined __ORBIS__ SceKernelUseconds timeoutMicrosecs; SceKernelUseconds* pTimeoutMicrosecs; if (timeoutMs == INFINITE) { pTimeoutMicrosecs = NULL; } else { timeoutMicrosecs = ((SceKernelUseconds)timeoutMs) * 1000; pTimeoutMicrosecs = &timeoutMicrosecs; } unsigned int bitmask = 0; for (int i = 0; i < m_size; i++) bitmask |= (1 << i); uint32_t waitMode = SCE_KERNEL_EVF_WAITMODE_AND; if (m_mode == e_modeAutoClear) { waitMode |= SCE_KERNEL_EVF_WAITMODE_CLEAR_PAT; } int err = sceKernelWaitEventFlag(m_events, bitmask, waitMode, NULL, pTimeoutMicrosecs); switch (err) { case SCE_OK: retVal = WAIT_OBJECT_0; break; case SCE_KERNEL_ERROR_ETIMEDOUT: retVal = WAIT_TIMEOUT; break; case SCE_KERNEL_ERROR_ECANCELED: retVal = WAIT_ABANDONED; break; default: retVal = WAIT_FAILED; break; } #elif defined __PSVITA__ SceUInt32 timeoutMicrosecs; SceUInt32* pTimeoutMicrosecs; if (timeoutMs == INFINITE) { pTimeoutMicrosecs = NULL; } else { timeoutMicrosecs = ((SceUInt32)timeoutMs) * 1000; pTimeoutMicrosecs = &timeoutMicrosecs; } unsigned int bitmask = 0; for (int i = 0; i < m_size; i++) bitmask |= (1 << i); uint32_t waitMode = SCE_KERNEL_EVF_WAITMODE_AND; if (m_mode == e_modeAutoClear) { waitMode |= SCE_KERNEL_EVF_WAITMODE_CLEAR_ALL; } int err = sceKernelWaitEventFlag(m_events, bitmask, waitMode, NULL, pTimeoutMicrosecs); switch (err) { case SCE_OK: return WAIT_OBJECT_0; case SCE_KERNEL_ERROR_WAIT_TIMEOUT: return WAIT_TIMEOUT; case SCE_KERNEL_ERROR_WAIT_CANCEL: return WAIT_ABANDONED; default: return WAIT_FAILED; } #else retVal = WaitForMultipleObjects(m_size, m_events, true, timeoutMs); #endif // __PS3__ return retVal; } std::uint32_t C4JThread::EventArray::WaitForAny(int timeoutMs) { #ifdef __PS3__ if (timeoutMs == INFINITE) timeoutMs = SYS_NO_TIMEOUT; int timoutMicrosecs = timeoutMs * 1000; unsigned int bitmask = 0; for (int i = 0; i < m_size; i++) bitmask |= (1 << i); uint32_t mode = SYS_EVENT_FLAG_WAIT_OR; if (m_mode == e_modeAutoClear) mode |= SYS_EVENT_FLAG_WAIT_CLEAR; int err = sys_event_flag_wait(m_events, bitmask, mode, 0, timoutMicrosecs); switch (err) { case CELL_OK: return WAIT_OBJECT_0; case ETIMEDOUT: return WAIT_TIMEOUT; case ECANCELED: return WAIT_ABANDONED; default: assert(0); return WAIT_FAILED; } #elif defined __ORBIS__ SceKernelUseconds timeoutMicrosecs; SceKernelUseconds* pTimeoutMicrosecs; if (timeoutMs == INFINITE) { pTimeoutMicrosecs = NULL; } else { timeoutMicrosecs = ((SceKernelUseconds)timeoutMs) * 1000; pTimeoutMicrosecs = &timeoutMicrosecs; } unsigned int bitmask = 0; for (int i = 0; i < m_size; i++) bitmask |= (1 << i); uint32_t waitMode = SCE_KERNEL_EVF_WAITMODE_OR; if (m_mode == e_modeAutoClear) { waitMode |= SCE_KERNEL_EVF_WAITMODE_CLEAR_PAT; } int err = sceKernelWaitEventFlag(m_events, bitmask, waitMode, NULL, pTimeoutMicrosecs); switch (err) { case SCE_OK: return WAIT_OBJECT_0; case SCE_KERNEL_ERROR_ETIMEDOUT: return WAIT_TIMEOUT; case SCE_KERNEL_ERROR_ECANCELED: return WAIT_ABANDONED; default: return WAIT_FAILED; } #elif defined __PSVITA__ SceUInt32 timeoutMicrosecs; SceUInt32* pTimeoutMicrosecs; if (timeoutMs == INFINITE) { pTimeoutMicrosecs = NULL; } else { timeoutMicrosecs = ((SceUInt32)timeoutMs) * 1000; pTimeoutMicrosecs = &timeoutMicrosecs; } unsigned int bitmask = 0; for (int i = 0; i < m_size; i++) bitmask |= (1 << i); uint32_t waitMode = SCE_KERNEL_EVF_WAITMODE_OR; if (m_mode == e_modeAutoClear) { waitMode |= SCE_KERNEL_EVF_WAITMODE_CLEAR_ALL; } int err = sceKernelWaitEventFlag(m_events, bitmask, waitMode, NULL, pTimeoutMicrosecs); switch (err) { case SCE_OK: return WAIT_OBJECT_0; case SCE_KERNEL_ERROR_WAIT_TIMEOUT: return WAIT_TIMEOUT; case SCE_KERNEL_ERROR_WAIT_CANCEL: return WAIT_ABANDONED; default: return WAIT_FAILED; } #else return WaitForMultipleObjects(m_size, m_events, false, timeoutMs); #endif // __PS3__ } #ifdef __PS3__ void C4JThread::EventArray::Cancel() { sys_event_flag_cancel(m_events, NULL); } #endif C4JThread::EventQueue::EventQueue(UpdateFunc* updateFunc, ThreadInitFunc threadInitFunc, const char* szThreadName) { m_updateFunc = updateFunc; m_threadInitFunc = threadInitFunc; strcpy(m_threadName, szThreadName); m_thread = NULL; m_startEvent = NULL; m_finishedEvent = NULL; m_processor = -1; m_priority = THREAD_PRIORITY_HIGHEST + 1; } void C4JThread::EventQueue::init() { m_startEvent = new C4JThread::EventArray(1); m_finishedEvent = new C4JThread::Event(); InitializeCriticalSection(&m_critSect); m_thread = new C4JThread(threadFunc, this, m_threadName); if (m_processor >= 0) m_thread->SetProcessor(m_processor); if (m_priority != THREAD_PRIORITY_HIGHEST + 1) m_thread->SetPriority(m_priority); m_thread->Run(); } void C4JThread::EventQueue::sendEvent(Level* pLevel) { if (m_thread == NULL) init(); EnterCriticalSection(&m_critSect); m_queue.push(pLevel); m_startEvent->Set(0); m_finishedEvent->Clear(); LeaveCriticalSection(&m_critSect); } void C4JThread::EventQueue::waitForFinish() { if (m_thread == NULL) init(); EnterCriticalSection(&m_critSect); if (m_queue.empty()) { LeaveCriticalSection((&m_critSect)); return; } LeaveCriticalSection((&m_critSect)); m_finishedEvent->WaitForSignal(INFINITE); } int C4JThread::EventQueue::threadFunc(void* lpParam) { EventQueue* p = (EventQueue*)lpParam; p->threadPoll(); return 0; } void C4JThread::EventQueue::threadPoll() { ShutdownManager::HasStarted(ShutdownManager::eEventQueueThreads, m_startEvent); if (m_threadInitFunc) m_threadInitFunc(); while (ShutdownManager::ShouldRun(ShutdownManager::eEventQueueThreads)) { std::uint32_t err = m_startEvent->WaitForAny(INFINITE); if (err == WAIT_OBJECT_0) { bool bListEmpty = true; do { EnterCriticalSection(&m_critSect); void* updateParam = m_queue.front(); LeaveCriticalSection(&m_critSect); m_updateFunc(updateParam); EnterCriticalSection(&m_critSect); m_queue.pop(); bListEmpty = m_queue.empty(); if (bListEmpty) { m_finishedEvent->Set(); } LeaveCriticalSection(&m_critSect); } while (!bListEmpty); } }; ShutdownManager::HasFinished(ShutdownManager::eEventQueueThreads); } #ifdef __ORBIS__ void C4JThread::PushAffinityAllCores() { assert(m_oldAffinityMask == 0); int err; ScePthread currThreadID = scePthreadSelf(); err = scePthreadGetaffinity(currThreadID, &m_oldAffinityMask); assert(err == SCE_OK); err = scePthreadSetaffinity(currThreadID, 63); assert(err == SCE_OK); } void C4JThread::PopAffinity() { int err; ScePthread currThreadID = scePthreadSelf(); err = scePthreadSetaffinity(currThreadID, m_oldAffinityMask); m_oldAffinityMask = 0; assert(err == SCE_OK); } #endif // __ORBIS__