diff --git a/Common/File/PathBrowser.cpp b/Common/File/PathBrowser.cpp index ae7d804ed1..f5ab1a33be 100644 --- a/Common/File/PathBrowser.cpp +++ b/Common/File/PathBrowser.cpp @@ -141,6 +141,8 @@ void PathBrowser::HandlePath() { pendingThread_ = std::thread([&] { SetCurrentThreadName("PathBrowser"); + AndroidJNIThreadContext jniContext; // destructor detaches + std::unique_lock guard(pendingLock_); std::vector results; Path lastPath("NONSENSE THAT WONT EQUAL A PATH"); @@ -177,8 +179,6 @@ void PathBrowser::HandlePath() { ready_ = true; } } - - DetachThreadFromJNI(); }); } diff --git a/Common/Net/HTTPClient.cpp b/Common/Net/HTTPClient.cpp index bac18da12d..7ef0c15df1 100644 --- a/Common/Net/HTTPClient.cpp +++ b/Common/Net/HTTPClient.cpp @@ -526,6 +526,9 @@ std::string Download::RedirectLocation(const std::string &baseUrl) { void Download::Do() { SetCurrentThreadName("Downloader::Do"); + + AndroidJNIThreadContext jniContext; + resultCode_ = 0; std::string downloadURL = url_; diff --git a/Common/Thread/ThreadManager.cpp b/Common/Thread/ThreadManager.cpp index 921831fa40..8aedb493e8 100644 --- a/Common/Thread/ThreadManager.cpp +++ b/Common/Thread/ThreadManager.cpp @@ -133,6 +133,10 @@ static void WorkerThreadFunc(GlobalThreadContext *global, ThreadContext *thread) } SetCurrentThreadName(thread->name); + if (thread->type == TaskType::IO_BLOCKING) { + AttachThreadToJNI(); + } + const bool isCompute = thread->type == TaskType::CPU_COMPUTE; const auto global_queue_size = [isCompute, &global]() -> int { return isCompute ? global->compute_queue_size.load() : global->io_queue_size.load(); @@ -187,7 +191,9 @@ static void WorkerThreadFunc(GlobalThreadContext *global, ThreadContext *thread) } // In case it got attached to JNI, detach it. Don't think this has any side effects if called redundantly. - DetachThreadFromJNI(); + if (thread->type == TaskType::IO_BLOCKING) { + DetachThreadFromJNI(); + } } void ThreadManager::Init(int numRealCores, int numLogicalCoresPerCpu) { diff --git a/Common/Thread/ThreadUtil.cpp b/Common/Thread/ThreadUtil.cpp index 43a44e82df..0e3716d6b6 100644 --- a/Common/Thread/ThreadUtil.cpp +++ b/Common/Thread/ThreadUtil.cpp @@ -67,6 +67,15 @@ static EXCEPTION_DISPOSITION NTAPI ignore_handler(EXCEPTION_RECORD *rec, } #endif +void AttachThreadToJNI() { +#if PPSSPP_PLATFORM(ANDROID) + Android_AttachThreadToJNI(); +#else + // Do nothing +#endif +} + + void DetachThreadFromJNI() { #if PPSSPP_PLATFORM(ANDROID) Android_DetachThreadFromJNI(); diff --git a/Common/Thread/ThreadUtil.h b/Common/Thread/ThreadUtil.h index a14c112f60..ee4b461401 100644 --- a/Common/Thread/ThreadUtil.h +++ b/Common/Thread/ThreadUtil.h @@ -14,13 +14,20 @@ const char *GetCurrentThreadName(); // exactly what it is is badly specified and not useful for anything. int GetCurrentThreadIdForDebug(); +// When you know that a thread potentially will make JNI calls, call this after setting its name. +void AttachThreadToJNI(); + // Call when leaving threads. On Android, calls DetachCurrentThread. // Threads that use scoped storage I/O end up attached as JNI threads, and will thus // need this in order to follow the rules correctly. Some devices seem to enforce this. void DetachThreadFromJNI(); +// Utility to call the above two functions. class AndroidJNIThreadContext { public: + AndroidJNIThreadContext() { + AttachThreadToJNI(); + } ~AndroidJNIThreadContext() { DetachThreadFromJNI(); } diff --git a/Core/Config.cpp b/Core/Config.cpp index 11ffc2fcd7..1a3c1ef40a 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -1736,6 +1736,9 @@ void Config::RemoveRecent(const std::string &file) { void Config::CleanRecent() { private_->SetRecentIsosThread([this] { SetCurrentThreadName("RecentISOs"); + + AndroidJNIThreadContext jniContext; // destructor detaches + double startTime = time_now_d(); std::lock_guard guard(private_->recentIsosLock); @@ -1766,8 +1769,6 @@ void Config::CleanRecent() { INFO_LOG(SYSTEM, "CleanRecent took %0.2f", time_now_d() - startTime); recentIsos = cleanedRecent; - - DetachThreadFromJNI(); }); } diff --git a/Core/FileLoaders/CachingFileLoader.cpp b/Core/FileLoaders/CachingFileLoader.cpp index 05fa5eefba..da350c1d09 100644 --- a/Core/FileLoaders/CachingFileLoader.cpp +++ b/Core/FileLoaders/CachingFileLoader.cpp @@ -269,6 +269,8 @@ void CachingFileLoader::StartReadAhead(s64 pos) { aheadThread_ = std::thread([this, pos] { SetCurrentThreadName("FileLoaderReadAhead"); + AndroidJNIThreadContext jniContext; + std::unique_lock guard(blocksMutex_); s64 cacheStartPos = pos >> BLOCK_SHIFT; s64 cacheEndPos = cacheStartPos + BLOCK_READAHEAD - 1; diff --git a/Core/FileLoaders/RamCachingFileLoader.cpp b/Core/FileLoaders/RamCachingFileLoader.cpp index 3e85777eec..c326bffa0b 100644 --- a/Core/FileLoaders/RamCachingFileLoader.cpp +++ b/Core/FileLoaders/RamCachingFileLoader.cpp @@ -227,6 +227,8 @@ void RamCachingFileLoader::StartReadAhead(s64 pos) { aheadThread_ = std::thread([this] { SetCurrentThreadName("FileLoaderReadAhead"); + AndroidJNIThreadContext jniContext; + while (aheadRemaining_ != 0 && !aheadCancel_) { // Where should we look? const u32 cacheStartPos = NextAheadBlock(); diff --git a/Core/HLE/sceIo.cpp b/Core/HLE/sceIo.cpp index 06fbdbf5d3..91ea6732d0 100644 --- a/Core/HLE/sceIo.cpp +++ b/Core/HLE/sceIo.cpp @@ -579,6 +579,7 @@ static void __IoAsyncEndCallback(SceUID threadID, SceUID prevCallbackId) { static void __IoManagerThread() { SetCurrentThreadName("IO"); + AndroidJNIThreadContext jniContext; while (ioManagerThreadEnabled && coreState != CORE_BOOT_ERROR && coreState != CORE_RUNTIME_ERROR && coreState != CORE_POWERDOWN) { ioManager.RunEventsUntil(CoreTiming::GetTicks() + msToCycles(1000)); } diff --git a/Core/HW/MemoryStick.cpp b/Core/HW/MemoryStick.cpp index 4f20352bd3..8511a9d76c 100644 --- a/Core/HW/MemoryStick.cpp +++ b/Core/HW/MemoryStick.cpp @@ -19,8 +19,10 @@ #include #include #include + #include "Common/Serialize/Serializer.h" #include "Common/Serialize/SerializeFuncs.h" +#include "Common/Thread/ThreadUtil.h" #include "Core/Config.h" #include "Core/CoreTiming.h" #include "Core/Compatibility.h" @@ -97,6 +99,10 @@ static void MemoryStick_CalcInitialFree() { std::unique_lock guard(freeCalcMutex); freeCalcStatus = FreeCalcStatus::RUNNING; freeCalcThread = std::thread([] { + SetCurrentThreadName("CalcInitialFree"); + + AndroidJNIThreadContext jniContext; + memstickInitialFree = pspFileSystem.FreeSpace("ms0:/") + pspFileSystem.ComputeRecursiveDirectorySize("ms0:/PSP/SAVEDATA/"); std::unique_lock guard(freeCalcMutex); diff --git a/Core/PSPLoaders.cpp b/Core/PSPLoaders.cpp index 0b14eeb843..ab060eb27b 100644 --- a/Core/PSPLoaders.cpp +++ b/Core/PSPLoaders.cpp @@ -307,7 +307,7 @@ bool Load_PSP_ISO(FileLoader *fileLoader, std::string *error_string) { //in case we didn't go through EmuScreen::boot g_Config.loadGameConfig(id, g_paramSFO.GetValueString("TITLE")); host->SendUIMessage("config_loaded", ""); - INFO_LOG(LOADER,"Loading %s...", bootpath.c_str()); + INFO_LOG(LOADER, "Loading %s...", bootpath.c_str()); PSPLoaders_Shutdown(); // Note: this thread reads the game binary, loads caches, and links HLE while UI spins. @@ -319,6 +319,8 @@ bool Load_PSP_ISO(FileLoader *fileLoader, std::string *error_string) { if (coreState != CORE_POWERUP) return; + AndroidJNIThreadContext jniContext; + PSP_SetLoading("Loading executable..."); // TODO: We can't use the initial error_string pointer. bool success = __KernelLoadExec(bootpath.c_str(), 0, &PSP_CoreParameter().errorString); @@ -455,6 +457,8 @@ bool Load_PSP_ELF_PBP(FileLoader *fileLoader, std::string *error_string) { if (coreState != CORE_POWERUP) return; + AndroidJNIThreadContext jniContext; + bool success = __KernelLoadExec(finalName.c_str(), 0, &PSP_CoreParameter().errorString); if (success && coreState == CORE_POWERUP) { coreState = PSP_CoreParameter().startBreak ? CORE_STEPPING : CORE_RUNNING; @@ -479,6 +483,8 @@ bool Load_PSP_GE_Dump(FileLoader *fileLoader, std::string *error_string) { if (coreState != CORE_POWERUP) return; + AndroidJNIThreadContext jniContext; + bool success = __KernelLoadGEDump("disc0:/data.ppdmp", &PSP_CoreParameter().errorString); if (success && coreState == CORE_POWERUP) { coreState = PSP_CoreParameter().startBreak ? CORE_STEPPING : CORE_RUNNING; diff --git a/Core/Reporting.cpp b/Core/Reporting.cpp index 7263f6f009..7a552a5341 100644 --- a/Core/Reporting.cpp +++ b/Core/Reporting.cpp @@ -111,6 +111,8 @@ namespace Reporting static int CalculateCRCThread() { SetCurrentThreadName("ReportCRC"); + AndroidJNIThreadContext jniContext; + FileLoader *fileLoader = ResolveFileLoaderTarget(ConstructFileLoader(crcFilename)); BlockDevice *blockDevice = constructBlockDevice(fileLoader); diff --git a/Core/SaveState.cpp b/Core/SaveState.cpp index 148e4307d1..d9282037a6 100644 --- a/Core/SaveState.cpp +++ b/Core/SaveState.cpp @@ -164,6 +164,9 @@ namespace SaveState compressThread_.join(); compressThread_ = std::thread([=]{ SetCurrentThreadName("SaveStateCompress"); + + AndroidJNIThreadContext jniContext; + Compress(*result, *state, *base); }); } diff --git a/Core/WebServer.cpp b/Core/WebServer.cpp index 4aff212eba..701d4f070c 100644 --- a/Core/WebServer.cpp +++ b/Core/WebServer.cpp @@ -314,6 +314,8 @@ static void ForwardDebuggerRequest(const http::Request &request) { static void ExecuteWebServer() { SetCurrentThreadName("HTTPServer"); + AndroidJNIThreadContext context; // Destructor detaches. + auto http = new http::Server(new NewThreadExecutor()); http->RegisterHandler("/", &HandleListing); // This lists all the (current) recent ISOs. diff --git a/UI/RemoteISOScreen.cpp b/UI/RemoteISOScreen.cpp index 33a212f34f..98cea5ea85 100644 --- a/UI/RemoteISOScreen.cpp +++ b/UI/RemoteISOScreen.cpp @@ -30,6 +30,7 @@ #include "Common/Net/HTTPClient.h" #include "Common/Net/Resolve.h" #include "Common/Net/URL.h" +#include "Common/Thread/ThreadUtil.h" #include "Common/File/PathBrowser.h" #include "Common/Data/Format/JSONReader.h" @@ -366,6 +367,7 @@ RemoteISOConnectScreen::RemoteISOConnectScreen() { scanAborted = false; scanThread_ = new std::thread([](RemoteISOConnectScreen *thiz) { + SetCurrentThreadName("RemoteISOScan"); thiz->ExecuteScan(); }, this); } diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index 9164cdac6a..276e4c3f4a 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -236,17 +236,7 @@ void AndroidLogger::Log(const LogMessage &message) { JNIEnv* getEnv() { JNIEnv *env; int status = gJvm->GetEnv((void**)&env, JNI_VERSION_1_6); - if (status < 0) { - // TODO: We should have a version of getEnv that doesn't allow auto-attach. - INFO_LOG(SYSTEM, "Thread '%s' not attached to JVM, attaching.", GetCurrentThreadName()); - JavaVMAttachArgs args{}; - args.version = JNI_VERSION_1_6; - args.name = GetCurrentThreadName(); - status = gJvm->AttachCurrentThread(&env, &args); - if (status < 0) { - return nullptr; - } - } + _assert_msg_(status >= 0, "'%s': Can only call getEnv if you've attached the thread already!", GetCurrentThreadName()); return env; } @@ -270,9 +260,31 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *pjvm, void *reserved) { return JNI_VERSION_1_6; } +void Android_AttachThreadToJNI() { + JNIEnv *env; + int status = gJvm->GetEnv((void **)&env, JNI_VERSION_1_6); + if (status < 0) { + // TODO: We should have a version of getEnv that doesn't allow auto-attach. + INFO_LOG(SYSTEM, "Attaching thread '%s' (not already attached) to JNI.", GetCurrentThreadName()); + JavaVMAttachArgs args{}; + args.version = JNI_VERSION_1_6; + args.name = GetCurrentThreadName(); + status = gJvm->AttachCurrentThread(&env, &args); + + if (status < 0) { + // bad, but wh + } + } else { + WARN_LOG(SYSTEM, "Thread %s was already attached to JNI.", GetCurrentThreadName()); + } +} + void Android_DetachThreadFromJNI() { - INFO_LOG(SYSTEM, "Detaching thread from JNI: '%s'", GetCurrentThreadName()); - gJvm->DetachCurrentThread(); + if (gJvm->DetachCurrentThread() == JNI_OK) { + INFO_LOG(SYSTEM, "Detached thread from JNI: '%s'", GetCurrentThreadName()); + } else { + WARN_LOG(SYSTEM, "Failed to detach thread '%s' from JNI - never attached?", GetCurrentThreadName()); + } } // Only used in OpenGL mode. diff --git a/android/jni/app-android.h b/android/jni/app-android.h index 8c0deff287..0dc6c03123 100644 --- a/android/jni/app-android.h +++ b/android/jni/app-android.h @@ -27,6 +27,7 @@ public: }; #endif +void Android_AttachThreadToJNI(); void Android_DetachThreadFromJNI(); #endif