Strict mode checking - no way to forget detaching now.

And if we forget to attach, boom. Hopefully I caught all of them.
This commit is contained in:
Henrik Rydgård 2023-01-03 23:29:22 +01:00
parent 916404f0e4
commit b56eef487c
17 changed files with 84 additions and 19 deletions

View file

@ -141,6 +141,8 @@ void PathBrowser::HandlePath() {
pendingThread_ = std::thread([&] {
SetCurrentThreadName("PathBrowser");
AndroidJNIThreadContext jniContext; // destructor detaches
std::unique_lock<std::mutex> guard(pendingLock_);
std::vector<File::FileInfo> results;
Path lastPath("NONSENSE THAT WONT EQUAL A PATH");
@ -177,8 +179,6 @@ void PathBrowser::HandlePath() {
ready_ = true;
}
}
DetachThreadFromJNI();
});
}

View file

@ -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_;

View file

@ -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) {

View file

@ -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();

View file

@ -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();
}

View file

@ -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<std::mutex> guard(private_->recentIsosLock);
@ -1766,8 +1769,6 @@ void Config::CleanRecent() {
INFO_LOG(SYSTEM, "CleanRecent took %0.2f", time_now_d() - startTime);
recentIsos = cleanedRecent;
DetachThreadFromJNI();
});
}

View file

@ -269,6 +269,8 @@ void CachingFileLoader::StartReadAhead(s64 pos) {
aheadThread_ = std::thread([this, pos] {
SetCurrentThreadName("FileLoaderReadAhead");
AndroidJNIThreadContext jniContext;
std::unique_lock<std::recursive_mutex> guard(blocksMutex_);
s64 cacheStartPos = pos >> BLOCK_SHIFT;
s64 cacheEndPos = cacheStartPos + BLOCK_READAHEAD - 1;

View file

@ -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();

View file

@ -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));
}

View file

@ -19,8 +19,10 @@
#include <condition_variable>
#include <mutex>
#include <thread>
#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<std::mutex> guard(freeCalcMutex);
freeCalcStatus = FreeCalcStatus::RUNNING;
freeCalcThread = std::thread([] {
SetCurrentThreadName("CalcInitialFree");
AndroidJNIThreadContext jniContext;
memstickInitialFree = pspFileSystem.FreeSpace("ms0:/") + pspFileSystem.ComputeRecursiveDirectorySize("ms0:/PSP/SAVEDATA/");
std::unique_lock<std::mutex> guard(freeCalcMutex);

View file

@ -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;

View file

@ -111,6 +111,8 @@ namespace Reporting
static int CalculateCRCThread() {
SetCurrentThreadName("ReportCRC");
AndroidJNIThreadContext jniContext;
FileLoader *fileLoader = ResolveFileLoaderTarget(ConstructFileLoader(crcFilename));
BlockDevice *blockDevice = constructBlockDevice(fileLoader);

View file

@ -164,6 +164,9 @@ namespace SaveState
compressThread_.join();
compressThread_ = std::thread([=]{
SetCurrentThreadName("SaveStateCompress");
AndroidJNIThreadContext jniContext;
Compress(*result, *state, *base);
});
}

View file

@ -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.

View file

@ -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);
}

View file

@ -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.

View file

@ -27,6 +27,7 @@ public:
};
#endif
void Android_AttachThreadToJNI();
void Android_DetachThreadFromJNI();
#endif