mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-11-23 01:19:16 +01:00
Move from dependency on JNI and Implement AtomicMutex
This commit is the start of moving towards a lockless and faster kernel which can run multiple independent threads with fast userspace synchronization.
This commit is contained in:
parent
3e9bfaec0e
commit
48d47a2b25
@ -5,8 +5,9 @@
|
||||
#include <csignal>
|
||||
|
||||
bool Halt;
|
||||
jobject Surface;
|
||||
uint FaultCount;
|
||||
std::mutex jniMtx;
|
||||
skyline::Mutex jniMtx;
|
||||
|
||||
void signalHandler(int signal) {
|
||||
syslog(LOG_ERR, "Halting program due to signal: %s", strsignal(signal));
|
||||
@ -54,10 +55,19 @@ extern "C" JNIEXPORT void Java_emu_skyline_GameActivity_executeRom(JNIEnv *env,
|
||||
logger->Info("Done in: {} ms", (std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count()));
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void Java_emu_skyline_GameActivity_lockMutex(JNIEnv *env, jobject instance) {
|
||||
extern "C" JNIEXPORT void Java_emu_skyline_GameActivity_setHalt(JNIEnv *env, jobject instance, jboolean halt) {
|
||||
jniMtx.lock();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void Java_emu_skyline_GameActivity_unlockMutex(JNIEnv *env, jobject instance) {
|
||||
Halt = halt;
|
||||
jniMtx.unlock();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void Java_emu_skyline_GameActivity_setSurface(JNIEnv *env, jobject instance, jobject surface) {
|
||||
jniMtx.lock();
|
||||
if(!env->IsSameObject(Surface, nullptr))
|
||||
env->DeleteGlobalRef(Surface);
|
||||
if(!env->IsSameObject(surface, nullptr))
|
||||
Surface = env->NewGlobalRef(surface);
|
||||
else
|
||||
Surface = surface;
|
||||
jniMtx.unlock();
|
||||
}
|
||||
|
@ -4,6 +4,21 @@
|
||||
#include <tinyxml2.h>
|
||||
|
||||
namespace skyline {
|
||||
void Mutex::lock() {
|
||||
while (flag.exchange(true, std::memory_order_relaxed));
|
||||
std::atomic_thread_fence(std::memory_order_acquire);
|
||||
}
|
||||
|
||||
void Mutex::unlock() {
|
||||
std::atomic_thread_fence(std::memory_order_release);
|
||||
flag.store(false, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
bool Mutex::try_lock() {
|
||||
bool fal = false;
|
||||
return flag.compare_exchange_strong(fal, true, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
Settings::Settings(const int preferenceFd) {
|
||||
tinyxml2::XMLDocument pref;
|
||||
if (pref.LoadFile(fdopen(preferenceFd, "r")))
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <vector>
|
||||
#include <fstream>
|
||||
#include <syslog.h>
|
||||
#import <thread>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <memory>
|
||||
@ -113,7 +114,31 @@ namespace skyline {
|
||||
enum class Sreg { Sp, Pc, PState };
|
||||
|
||||
/**
|
||||
* @brief The Logger class is to write log output
|
||||
* @brief The Mutex class is a wrapper around an atomic bool used for synchronization
|
||||
*/
|
||||
class Mutex {
|
||||
std::atomic<bool> flag{false}; //!< This atomic bool holds the status of the lock
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Wait on and lock the mutex
|
||||
*/
|
||||
void lock();
|
||||
|
||||
/**
|
||||
* @brief Lock the mutex if it is unlocked else return
|
||||
* @return If the mutex was successfully locked or not
|
||||
*/
|
||||
bool try_lock();
|
||||
|
||||
/**
|
||||
* @brief Unlock the mutex if it is held by this thread
|
||||
*/
|
||||
void unlock();
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The Logger class is to write log output to file and logcat
|
||||
*/
|
||||
class Logger {
|
||||
private:
|
||||
|
@ -8,9 +8,10 @@
|
||||
#include <android/native_window_jni.h>
|
||||
|
||||
extern bool Halt;
|
||||
extern jobject Surface;
|
||||
|
||||
namespace skyline::gpu {
|
||||
GPU::GPU(const DeviceState &state) : state(state), window(ANativeWindow_fromSurface(state.jvmManager->env, state.jvmManager->GetField("surface", "Landroid/view/Surface;"))), bufferQueue(state), vsyncEvent(std::make_shared<kernel::type::KEvent>(state)), bufferEvent(std::make_shared<kernel::type::KEvent>(state)) {
|
||||
GPU::GPU(const DeviceState &state) : state(state), window(ANativeWindow_fromSurface(state.jvmManager->GetEnv(), Surface)), bufferQueue(state), vsyncEvent(std::make_shared<kernel::type::KEvent>(state)), bufferEvent(std::make_shared<kernel::type::KEvent>(state)) {
|
||||
ANativeWindow_acquire(window);
|
||||
resolution.width = static_cast<u32>(ANativeWindow_getWidth(window));
|
||||
resolution.height = static_cast<u32>(ANativeWindow_getHeight(window));
|
||||
@ -23,16 +24,16 @@ namespace skyline::gpu {
|
||||
|
||||
void GPU::Loop() {
|
||||
if (surfaceUpdate) {
|
||||
if (!state.jvmManager->CheckNull("surface", "Landroid/view/Surface;")) {
|
||||
window = ANativeWindow_fromSurface(state.jvmManager->env, state.jvmManager->GetField("surface", "Landroid/view/Surface;"));
|
||||
if (state.jvmManager->CheckNull(Surface))
|
||||
return;
|
||||
window = ANativeWindow_fromSurface(state.jvmManager->GetEnv(), Surface);
|
||||
ANativeWindow_acquire(window);
|
||||
resolution.width = static_cast<u32>(ANativeWindow_getWidth(window));
|
||||
resolution.height = static_cast<u32>(ANativeWindow_getHeight(window));
|
||||
format = ANativeWindow_getFormat(window);
|
||||
surfaceUpdate = true;
|
||||
} else
|
||||
return;
|
||||
} else
|
||||
surfaceUpdate = state.jvmManager->CheckNull("surface", "Landroid/view/Surface;");
|
||||
surfaceUpdate = state.jvmManager->CheckNull(Surface);
|
||||
if (!bufferQueue.displayQueue.empty()) {
|
||||
auto &buffer = bufferQueue.displayQueue.front();
|
||||
bufferQueue.displayQueue.pop();
|
||||
|
@ -1,7 +1,27 @@
|
||||
#include "jvm.h"
|
||||
|
||||
thread_local JNIEnv *env;
|
||||
|
||||
namespace skyline {
|
||||
JvmManager::JvmManager(JNIEnv *env, jobject instance) : env(env), instance(instance), instanceClass(env->GetObjectClass(instance)) {}
|
||||
JvmManager::JvmManager(JNIEnv *environ, jobject instance) : instance(instance), instanceClass(reinterpret_cast<jclass>(environ->NewGlobalRef(environ->GetObjectClass(instance)))) {
|
||||
env = environ;
|
||||
if(env->GetJavaVM(&vm) < 0)
|
||||
throw exception("Cannot get JavaVM from environment");
|
||||
}
|
||||
|
||||
void JvmManager::AttachThread() {
|
||||
if(!env)
|
||||
vm->AttachCurrentThread(&env, nullptr);
|
||||
}
|
||||
|
||||
void JvmManager::DetachThread() {
|
||||
if(env)
|
||||
vm->DetachCurrentThread();
|
||||
}
|
||||
|
||||
JNIEnv* JvmManager::GetEnv() {
|
||||
return env;
|
||||
}
|
||||
|
||||
jobject JvmManager::GetField(const char *key, const char *signature) {
|
||||
return env->GetObjectField(instance, env->GetFieldID(instanceClass, key, signature));
|
||||
@ -10,4 +30,8 @@ namespace skyline {
|
||||
bool JvmManager::CheckNull(const char *key, const char *signature) {
|
||||
return env->IsSameObject(env->GetObjectField(instance, env->GetFieldID(instanceClass, key, signature)), nullptr);
|
||||
}
|
||||
|
||||
bool JvmManager::CheckNull(jobject& object) {
|
||||
return env->IsSameObject(object, nullptr);
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ namespace skyline {
|
||||
*/
|
||||
class JvmManager {
|
||||
public:
|
||||
JNIEnv *env; //!< A pointer to the JNI environment
|
||||
JavaVM *vm{}; //!< A pointer to the Java VM
|
||||
jobject instance; //!< A reference to the activity
|
||||
jclass instanceClass; //!< The class of the activity
|
||||
|
||||
@ -19,6 +19,21 @@ namespace skyline {
|
||||
*/
|
||||
JvmManager(JNIEnv *env, jobject instance);
|
||||
|
||||
/**
|
||||
* @brief Attach the current thread to the Java VM
|
||||
*/
|
||||
void AttachThread();
|
||||
|
||||
/**
|
||||
* @brief Detach the current thread to the Java VM
|
||||
*/
|
||||
void DetachThread();
|
||||
|
||||
/**
|
||||
* @brief Returns a pointer to the JNI environment for the current thread
|
||||
*/
|
||||
JNIEnv* GetEnv();
|
||||
|
||||
/**
|
||||
* @brief Retrieves a specific field of the given type from the activity
|
||||
* @tparam objectType The type of the object in the field
|
||||
@ -27,6 +42,7 @@ namespace skyline {
|
||||
*/
|
||||
template<typename objectType>
|
||||
inline objectType GetField(const char *key) {
|
||||
JNIEnv *env = GetEnv();
|
||||
if constexpr(std::is_same<objectType, jboolean>())
|
||||
return env->GetBooleanField(instance, env->GetFieldID(instanceClass, key, "Z"));
|
||||
else if constexpr(std::is_same<objectType, jbyte>())
|
||||
@ -60,5 +76,12 @@ namespace skyline {
|
||||
* @return If the field is null or not
|
||||
*/
|
||||
bool CheckNull(const char *key, const char *signature);
|
||||
|
||||
/**
|
||||
* @brief Checks if a specific jobject is null or not
|
||||
* @param object The jobject to check
|
||||
* @return If the object is null or not
|
||||
*/
|
||||
bool CheckNull(jobject &object);
|
||||
};
|
||||
}
|
||||
|
@ -6,10 +6,9 @@
|
||||
#include "guest.h"
|
||||
|
||||
extern bool Halt;
|
||||
extern std::mutex jniMtx;
|
||||
extern skyline::Mutex jniMtx;
|
||||
|
||||
namespace skyline {
|
||||
|
||||
namespace instr {
|
||||
/**
|
||||
* @brief A bit-field struct that encapsulates a BRK instruction. See https://developer.arm.com/docs/ddi0596/latest/base-instructions-alphabetic-order/brk-breakpoint-instruction.
|
||||
@ -205,7 +204,7 @@ namespace skyline {
|
||||
void NCE::Execute() {
|
||||
int status = 0;
|
||||
while (!Halt && !state.os->processMap.empty()) {
|
||||
jniMtx.lock();
|
||||
std::lock_guard jniGd(jniMtx);
|
||||
for (const auto &process : state.os->processMap) {
|
||||
state.os->thisProcess = process.second;
|
||||
state.os->thisThread = process.second->threadMap.at(process.first);
|
||||
@ -263,8 +262,6 @@ namespace skyline {
|
||||
}
|
||||
state.os->serviceManager.Loop();
|
||||
state.gpu->Loop();
|
||||
Halt = state.jvmManager->GetField<jboolean>("halt");
|
||||
jniMtx.unlock();
|
||||
}
|
||||
for (const auto &process : state.os->processMap) {
|
||||
state.os->KillThread(process.first);
|
||||
@ -406,8 +403,7 @@ namespace skyline {
|
||||
u32 *end = address + (code.size() / sizeof(u32));
|
||||
i64 patchOffset = offset;
|
||||
|
||||
std::vector<u32> patch;
|
||||
patch.resize((guest::saveCtxSize + guest::loadCtxSize) / sizeof(u32));
|
||||
std::vector<u32> patch((guest::saveCtxSize + guest::loadCtxSize) / sizeof(u32));
|
||||
std::memcpy(patch.data(), reinterpret_cast<void*>(&guest::saveCtx), guest::saveCtxSize);
|
||||
offset += guest::saveCtxSize;
|
||||
|
||||
|
@ -24,13 +24,12 @@ class GameActivity : AppCompatActivity(), SurfaceHolder.Callback, InputQueue.Cal
|
||||
private lateinit var preferenceFd: ParcelFileDescriptor
|
||||
private lateinit var logFd: ParcelFileDescriptor
|
||||
private var surface: Surface? = null
|
||||
private var inputQueue: Long? = null
|
||||
private var inputQueue: Long = 0L
|
||||
private lateinit var gameThread: Thread
|
||||
private var halt: Boolean = false
|
||||
|
||||
private external fun executeRom(romString: String, romType: Int, romFd: Int, preferenceFd: Int, logFd: Int)
|
||||
private external fun lockMutex()
|
||||
private external fun unlockMutex()
|
||||
private external fun setHalt(halt: Boolean)
|
||||
private external fun setSurface(surface: Surface?)
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
@ -56,7 +55,7 @@ class GameActivity : AppCompatActivity(), SurfaceHolder.Callback, InputQueue.Cal
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
halt = true
|
||||
setHalt(true)
|
||||
gameThread.join()
|
||||
romFd.close()
|
||||
preferenceFd.close()
|
||||
@ -65,9 +64,8 @@ class GameActivity : AppCompatActivity(), SurfaceHolder.Callback, InputQueue.Cal
|
||||
|
||||
override fun surfaceCreated(holder: SurfaceHolder?) {
|
||||
Log.d("surfaceCreated", "Holder: ${holder.toString()}")
|
||||
lockMutex()
|
||||
surface = holder!!.surface
|
||||
unlockMutex()
|
||||
setSurface(surface)
|
||||
}
|
||||
|
||||
override fun surfaceChanged(holder: SurfaceHolder?, format: Int, width: Int, height: Int) {
|
||||
@ -76,9 +74,8 @@ class GameActivity : AppCompatActivity(), SurfaceHolder.Callback, InputQueue.Cal
|
||||
|
||||
override fun surfaceDestroyed(holder: SurfaceHolder?) {
|
||||
Log.d("surfaceDestroyed", "Holder: ${holder.toString()}")
|
||||
lockMutex()
|
||||
surface = null
|
||||
unlockMutex()
|
||||
setSurface(surface)
|
||||
}
|
||||
|
||||
override fun onInputQueueCreated(queue: InputQueue?) {
|
||||
@ -86,10 +83,12 @@ class GameActivity : AppCompatActivity(), SurfaceHolder.Callback, InputQueue.Cal
|
||||
val clazz = Class.forName("android.view.InputQueue")
|
||||
val method: Method = clazz.getMethod("getNativePtr")
|
||||
inputQueue = method.invoke(queue)!! as Long
|
||||
//setQueue(inputQueue)
|
||||
}
|
||||
|
||||
override fun onInputQueueDestroyed(queue: InputQueue?) {
|
||||
Log.d("onInputQueueDestroyed", "InputQueue: ${queue.toString()}")
|
||||
inputQueue = null
|
||||
inputQueue = 0L
|
||||
//setQueue(inputQueue)
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ buildscript {
|
||||
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.5.2'
|
||||
classpath 'com.android.tools.build:gradle:3.5.3'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
|
Loading…
Reference in New Issue
Block a user