mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-11-30 01:54:17 +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>
|
#include <csignal>
|
||||||
|
|
||||||
bool Halt;
|
bool Halt;
|
||||||
|
jobject Surface;
|
||||||
uint FaultCount;
|
uint FaultCount;
|
||||||
std::mutex jniMtx;
|
skyline::Mutex jniMtx;
|
||||||
|
|
||||||
void signalHandler(int signal) {
|
void signalHandler(int signal) {
|
||||||
syslog(LOG_ERR, "Halting program due to signal: %s", strsignal(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()));
|
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();
|
jniMtx.lock();
|
||||||
}
|
Halt = halt;
|
||||||
|
jniMtx.unlock();
|
||||||
extern "C" JNIEXPORT void Java_emu_skyline_GameActivity_unlockMutex(JNIEnv *env, jobject instance) {
|
}
|
||||||
|
|
||||||
|
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();
|
jniMtx.unlock();
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,21 @@
|
|||||||
#include <tinyxml2.h>
|
#include <tinyxml2.h>
|
||||||
|
|
||||||
namespace skyline {
|
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) {
|
Settings::Settings(const int preferenceFd) {
|
||||||
tinyxml2::XMLDocument pref;
|
tinyxml2::XMLDocument pref;
|
||||||
if (pref.LoadFile(fdopen(preferenceFd, "r")))
|
if (pref.LoadFile(fdopen(preferenceFd, "r")))
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <syslog.h>
|
#include <syslog.h>
|
||||||
|
#import <thread>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@ -113,7 +114,31 @@ namespace skyline {
|
|||||||
enum class Sreg { Sp, Pc, PState };
|
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 {
|
class Logger {
|
||||||
private:
|
private:
|
||||||
|
@ -8,9 +8,10 @@
|
|||||||
#include <android/native_window_jni.h>
|
#include <android/native_window_jni.h>
|
||||||
|
|
||||||
extern bool Halt;
|
extern bool Halt;
|
||||||
|
extern jobject Surface;
|
||||||
|
|
||||||
namespace skyline::gpu {
|
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);
|
ANativeWindow_acquire(window);
|
||||||
resolution.width = static_cast<u32>(ANativeWindow_getWidth(window));
|
resolution.width = static_cast<u32>(ANativeWindow_getWidth(window));
|
||||||
resolution.height = static_cast<u32>(ANativeWindow_getHeight(window));
|
resolution.height = static_cast<u32>(ANativeWindow_getHeight(window));
|
||||||
@ -23,16 +24,16 @@ namespace skyline::gpu {
|
|||||||
|
|
||||||
void GPU::Loop() {
|
void GPU::Loop() {
|
||||||
if (surfaceUpdate) {
|
if (surfaceUpdate) {
|
||||||
if (!state.jvmManager->CheckNull("surface", "Landroid/view/Surface;")) {
|
if (state.jvmManager->CheckNull(Surface))
|
||||||
window = ANativeWindow_fromSurface(state.jvmManager->env, state.jvmManager->GetField("surface", "Landroid/view/Surface;"));
|
|
||||||
ANativeWindow_acquire(window);
|
|
||||||
resolution.width = static_cast<u32>(ANativeWindow_getWidth(window));
|
|
||||||
resolution.height = static_cast<u32>(ANativeWindow_getHeight(window));
|
|
||||||
format = ANativeWindow_getFormat(window);
|
|
||||||
} else
|
|
||||||
return;
|
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
|
} else
|
||||||
surfaceUpdate = state.jvmManager->CheckNull("surface", "Landroid/view/Surface;");
|
surfaceUpdate = state.jvmManager->CheckNull(Surface);
|
||||||
if (!bufferQueue.displayQueue.empty()) {
|
if (!bufferQueue.displayQueue.empty()) {
|
||||||
auto &buffer = bufferQueue.displayQueue.front();
|
auto &buffer = bufferQueue.displayQueue.front();
|
||||||
bufferQueue.displayQueue.pop();
|
bufferQueue.displayQueue.pop();
|
||||||
|
@ -1,7 +1,27 @@
|
|||||||
#include "jvm.h"
|
#include "jvm.h"
|
||||||
|
|
||||||
|
thread_local JNIEnv *env;
|
||||||
|
|
||||||
namespace skyline {
|
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) {
|
jobject JvmManager::GetField(const char *key, const char *signature) {
|
||||||
return env->GetObjectField(instance, env->GetFieldID(instanceClass, key, 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) {
|
bool JvmManager::CheckNull(const char *key, const char *signature) {
|
||||||
return env->IsSameObject(env->GetObjectField(instance, env->GetFieldID(instanceClass, key, signature)), nullptr);
|
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 {
|
class JvmManager {
|
||||||
public:
|
public:
|
||||||
JNIEnv *env; //!< A pointer to the JNI environment
|
JavaVM *vm{}; //!< A pointer to the Java VM
|
||||||
jobject instance; //!< A reference to the activity
|
jobject instance; //!< A reference to the activity
|
||||||
jclass instanceClass; //!< The class of the activity
|
jclass instanceClass; //!< The class of the activity
|
||||||
|
|
||||||
@ -19,6 +19,21 @@ namespace skyline {
|
|||||||
*/
|
*/
|
||||||
JvmManager(JNIEnv *env, jobject instance);
|
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
|
* @brief Retrieves a specific field of the given type from the activity
|
||||||
* @tparam objectType The type of the object in the field
|
* @tparam objectType The type of the object in the field
|
||||||
@ -27,6 +42,7 @@ namespace skyline {
|
|||||||
*/
|
*/
|
||||||
template<typename objectType>
|
template<typename objectType>
|
||||||
inline objectType GetField(const char *key) {
|
inline objectType GetField(const char *key) {
|
||||||
|
JNIEnv *env = GetEnv();
|
||||||
if constexpr(std::is_same<objectType, jboolean>())
|
if constexpr(std::is_same<objectType, jboolean>())
|
||||||
return env->GetBooleanField(instance, env->GetFieldID(instanceClass, key, "Z"));
|
return env->GetBooleanField(instance, env->GetFieldID(instanceClass, key, "Z"));
|
||||||
else if constexpr(std::is_same<objectType, jbyte>())
|
else if constexpr(std::is_same<objectType, jbyte>())
|
||||||
@ -60,5 +76,12 @@ namespace skyline {
|
|||||||
* @return If the field is null or not
|
* @return If the field is null or not
|
||||||
*/
|
*/
|
||||||
bool CheckNull(const char *key, const char *signature);
|
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"
|
#include "guest.h"
|
||||||
|
|
||||||
extern bool Halt;
|
extern bool Halt;
|
||||||
extern std::mutex jniMtx;
|
extern skyline::Mutex jniMtx;
|
||||||
|
|
||||||
namespace skyline {
|
namespace skyline {
|
||||||
|
|
||||||
namespace instr {
|
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.
|
* @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() {
|
void NCE::Execute() {
|
||||||
int status = 0;
|
int status = 0;
|
||||||
while (!Halt && !state.os->processMap.empty()) {
|
while (!Halt && !state.os->processMap.empty()) {
|
||||||
jniMtx.lock();
|
std::lock_guard jniGd(jniMtx);
|
||||||
for (const auto &process : state.os->processMap) {
|
for (const auto &process : state.os->processMap) {
|
||||||
state.os->thisProcess = process.second;
|
state.os->thisProcess = process.second;
|
||||||
state.os->thisThread = process.second->threadMap.at(process.first);
|
state.os->thisThread = process.second->threadMap.at(process.first);
|
||||||
@ -263,8 +262,6 @@ namespace skyline {
|
|||||||
}
|
}
|
||||||
state.os->serviceManager.Loop();
|
state.os->serviceManager.Loop();
|
||||||
state.gpu->Loop();
|
state.gpu->Loop();
|
||||||
Halt = state.jvmManager->GetField<jboolean>("halt");
|
|
||||||
jniMtx.unlock();
|
|
||||||
}
|
}
|
||||||
for (const auto &process : state.os->processMap) {
|
for (const auto &process : state.os->processMap) {
|
||||||
state.os->KillThread(process.first);
|
state.os->KillThread(process.first);
|
||||||
@ -406,8 +403,7 @@ namespace skyline {
|
|||||||
u32 *end = address + (code.size() / sizeof(u32));
|
u32 *end = address + (code.size() / sizeof(u32));
|
||||||
i64 patchOffset = offset;
|
i64 patchOffset = offset;
|
||||||
|
|
||||||
std::vector<u32> patch;
|
std::vector<u32> patch((guest::saveCtxSize + guest::loadCtxSize) / sizeof(u32));
|
||||||
patch.resize((guest::saveCtxSize + guest::loadCtxSize) / sizeof(u32));
|
|
||||||
std::memcpy(patch.data(), reinterpret_cast<void*>(&guest::saveCtx), guest::saveCtxSize);
|
std::memcpy(patch.data(), reinterpret_cast<void*>(&guest::saveCtx), guest::saveCtxSize);
|
||||||
offset += guest::saveCtxSize;
|
offset += guest::saveCtxSize;
|
||||||
|
|
||||||
|
@ -24,13 +24,12 @@ class GameActivity : AppCompatActivity(), SurfaceHolder.Callback, InputQueue.Cal
|
|||||||
private lateinit var preferenceFd: ParcelFileDescriptor
|
private lateinit var preferenceFd: ParcelFileDescriptor
|
||||||
private lateinit var logFd: ParcelFileDescriptor
|
private lateinit var logFd: ParcelFileDescriptor
|
||||||
private var surface: Surface? = null
|
private var surface: Surface? = null
|
||||||
private var inputQueue: Long? = null
|
private var inputQueue: Long = 0L
|
||||||
private lateinit var gameThread: Thread
|
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 executeRom(romString: String, romType: Int, romFd: Int, preferenceFd: Int, logFd: Int)
|
||||||
private external fun lockMutex()
|
private external fun setHalt(halt: Boolean)
|
||||||
private external fun unlockMutex()
|
private external fun setSurface(surface: Surface?)
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
@ -56,7 +55,7 @@ class GameActivity : AppCompatActivity(), SurfaceHolder.Callback, InputQueue.Cal
|
|||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
halt = true
|
setHalt(true)
|
||||||
gameThread.join()
|
gameThread.join()
|
||||||
romFd.close()
|
romFd.close()
|
||||||
preferenceFd.close()
|
preferenceFd.close()
|
||||||
@ -65,9 +64,8 @@ class GameActivity : AppCompatActivity(), SurfaceHolder.Callback, InputQueue.Cal
|
|||||||
|
|
||||||
override fun surfaceCreated(holder: SurfaceHolder?) {
|
override fun surfaceCreated(holder: SurfaceHolder?) {
|
||||||
Log.d("surfaceCreated", "Holder: ${holder.toString()}")
|
Log.d("surfaceCreated", "Holder: ${holder.toString()}")
|
||||||
lockMutex()
|
|
||||||
surface = holder!!.surface
|
surface = holder!!.surface
|
||||||
unlockMutex()
|
setSurface(surface)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun surfaceChanged(holder: SurfaceHolder?, format: Int, width: Int, height: Int) {
|
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?) {
|
override fun surfaceDestroyed(holder: SurfaceHolder?) {
|
||||||
Log.d("surfaceDestroyed", "Holder: ${holder.toString()}")
|
Log.d("surfaceDestroyed", "Holder: ${holder.toString()}")
|
||||||
lockMutex()
|
|
||||||
surface = null
|
surface = null
|
||||||
unlockMutex()
|
setSurface(surface)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onInputQueueCreated(queue: InputQueue?) {
|
override fun onInputQueueCreated(queue: InputQueue?) {
|
||||||
@ -86,10 +83,12 @@ class GameActivity : AppCompatActivity(), SurfaceHolder.Callback, InputQueue.Cal
|
|||||||
val clazz = Class.forName("android.view.InputQueue")
|
val clazz = Class.forName("android.view.InputQueue")
|
||||||
val method: Method = clazz.getMethod("getNativePtr")
|
val method: Method = clazz.getMethod("getNativePtr")
|
||||||
inputQueue = method.invoke(queue)!! as Long
|
inputQueue = method.invoke(queue)!! as Long
|
||||||
|
//setQueue(inputQueue)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onInputQueueDestroyed(queue: InputQueue?) {
|
override fun onInputQueueDestroyed(queue: InputQueue?) {
|
||||||
Log.d("onInputQueueDestroyed", "InputQueue: ${queue.toString()}")
|
Log.d("onInputQueueDestroyed", "InputQueue: ${queue.toString()}")
|
||||||
inputQueue = null
|
inputQueue = 0L
|
||||||
|
//setQueue(inputQueue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ buildscript {
|
|||||||
|
|
||||||
}
|
}
|
||||||
dependencies {
|
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"
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
|
|
||||||
// NOTE: Do not place your application dependencies here; they belong
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
|
Loading…
Reference in New Issue
Block a user