Skip to content

Commit

Permalink
Move from dependency on JNI and Implement AtomicMutex
Browse files Browse the repository at this point in the history
This commit is the start of moving towards a lockless and faster kernel which can run multiple independent threads with fast userspace synchronization.
  • Loading branch information
PixelyIon committed Feb 15, 2020
1 parent 3e9bfae commit 48d47a2
Show file tree
Hide file tree
Showing 9 changed files with 126 additions and 33 deletions.
16 changes: 13 additions & 3 deletions app/src/main/cpp/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down Expand Up @@ -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();
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();
}
15 changes: 15 additions & 0 deletions app/src/main/cpp/skyline/common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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")))
Expand Down
27 changes: 26 additions & 1 deletion app/src/main/cpp/skyline/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <vector>
#include <fstream>
#include <syslog.h>
#import <thread>
#include <string>
#include <sstream>
#include <memory>
Expand Down Expand Up @@ -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:
Expand Down
19 changes: 10 additions & 9 deletions app/src/main/cpp/skyline/gpu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand All @@ -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;"));
ANativeWindow_acquire(window);
resolution.width = static_cast<u32>(ANativeWindow_getWidth(window));
resolution.height = static_cast<u32>(ANativeWindow_getHeight(window));
format = ANativeWindow_getFormat(window);
} else
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
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();
Expand Down
26 changes: 25 additions & 1 deletion app/src/main/cpp/skyline/jvm.cpp
Original file line number Diff line number Diff line change
@@ -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));
Expand All @@ -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);
}
}
25 changes: 24 additions & 1 deletion app/src/main/cpp/skyline/jvm.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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
Expand All @@ -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>())
Expand Down Expand Up @@ -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);
};
}
10 changes: 3 additions & 7 deletions app/src/main/cpp/skyline/nce.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;

Expand Down
19 changes: 9 additions & 10 deletions app/src/main/java/emu/skyline/GameActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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()
Expand All @@ -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) {
Expand All @@ -76,20 +74,21 @@ 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?) {
Log.i("onInputQueueCreated", "InputQueue: ${queue.toString()}")
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)
}
}
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 48d47a2

Please sign in to comment.