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:
◱ PixelyIon 2019-12-26 23:40:29 +05:30 committed by ◱ PixelyIon
parent 3e9bfaec0e
commit 48d47a2b25
9 changed files with 128 additions and 35 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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