Skip to content

Instantly share code, notes, and snippets.

@jb55
Last active March 13, 2025 22:24
Show Gist options
  • Save jb55/e162d48309ef874cd34397fa820945d8 to your computer and use it in GitHub Desktop.
Save jb55/e162d48309ef874cd34397fa820945d8 to your computer and use it in GitHub Desktop.
commit 3552189390f2693fea6ed6acbcb08b1b17db0bd5
Author: Robert Bragg <[email protected]>
Date: Wed May 25 23:57:45 2022 +0100
glue: remove unused variable
diff --git a/game-activity/prefab-src/modules/game-activity/src/game-activity/native_app_glue/android_native_app_glue.c b/game-activity/prefab-src/modules/game-activity/src/game-activity/native_app_glue/android_native_app_glue.c
index ce6c7efdb8aa..626e7b82c10d 100644
--- a/game-activity/prefab-src/modules/game-activity/src/game-activity/native_app_glue/android_native_app_glue.c
+++ b/game-activity/prefab-src/modules/game-activity/src/game-activity/native_app_glue/android_native_app_glue.c
@@ -402,7 +402,6 @@ static void onSaveInstanceState(GameActivity* activity,
LOGV("SaveInstanceState: %p", activity);
struct android_app* android_app = ToApp(activity);
- void* savedState = NULL;
pthread_mutex_lock(&android_app->mutex);
android_app->stateSaved = 0;
android_app_write_cmd(android_app, APP_CMD_SAVE_STATE);
commit d68f8aff9ff655a68dfc2dabf2d9bfae2cc16c93
Author: Robert Bragg <[email protected]>
Date: Fri Jul 28 22:18:24 2023 +0100
glue: fix deadlocks in java callbacks after app destroyed
This ensures that any java Activity callbacks take into account the
possibility that the `android_app` may have already been marked
destroyed if `android_main` has returned - and so they mustn't block
and wait for a thread that is no longer running.
diff --git a/game-activity/prefab-src/modules/game-activity/src/game-activity/native_app_glue/android_native_app_glue.c b/game-activity/prefab-src/modules/game-activity/src/game-activity/native_app_glue/android_native_app_glue.c
index 626e7b82c10d..f1c135f40d01 100644
--- a/game-activity/prefab-src/modules/game-activity/src/game-activity/native_app_glue/android_native_app_glue.c
+++ b/game-activity/prefab-src/modules/game-activity/src/game-activity/native_app_glue/android_native_app_glue.c
@@ -327,6 +327,15 @@ static void android_app_set_window(struct android_app* android_app,
ANativeWindow* window) {
LOGV("android_app_set_window called");
pthread_mutex_lock(&android_app->mutex);
+
+ // NB: we have to consider that the native thread could have already
+ // (gracefully) exit (setting android_app->destroyed) and so we need
+ // to be careful to avoid a deadlock waiting for a thread that's
+ // already exit.
+ if (android_app->destroyed) {
+ pthread_mutex_unlock(&android_app->mutex);
+ return;
+ }
if (android_app->pendingWindow != NULL) {
android_app_write_cmd(android_app, APP_CMD_TERM_WINDOW);
}
@@ -343,9 +352,16 @@ static void android_app_set_window(struct android_app* android_app,
static void android_app_set_activity_state(struct android_app* android_app,
int8_t cmd) {
pthread_mutex_lock(&android_app->mutex);
- android_app_write_cmd(android_app, cmd);
- while (android_app->activityState != cmd) {
- pthread_cond_wait(&android_app->cond, &android_app->mutex);
+
+ // NB: we have to consider that the native thread could have already
+ // (gracefully) exit (setting android_app->destroyed) and so we need
+ // to be careful to avoid a deadlock waiting for a thread that's
+ // already exit.
+ if (!android_app->destroyed) {
+ android_app_write_cmd(android_app, cmd);
+ while (android_app->activityState != cmd) {
+ pthread_cond_wait(&android_app->cond, &android_app->mutex);
+ }
}
pthread_mutex_unlock(&android_app->mutex);
}
@@ -354,6 +370,14 @@ static void android_app_free(struct android_app* android_app) {
int input_buf_idx = 0;
pthread_mutex_lock(&android_app->mutex);
+
+ // It's possible that onDestroy is called after we have already 'destroyed'
+ // the app (via `android_app_destroy` due to `android_main` returning.
+ //
+ // In this case `->destroyed` will already be set (so we won't deadlock in
+ // the loop below) but we still need to close the messaging fds and finish
+ // freeing the android_app
+
android_app_write_cmd(android_app, APP_CMD_DESTROY);
while (!android_app->destroyed) {
pthread_cond_wait(&android_app->cond, &android_app->mutex);
@@ -403,6 +427,16 @@ static void onSaveInstanceState(GameActivity* activity,
struct android_app* android_app = ToApp(activity);
pthread_mutex_lock(&android_app->mutex);
+
+ // NB: we have to consider that the native thread could have already
+ // (gracefully) exit (setting android_app->destroyed) and so we need
+ // to be careful to avoid a deadlock waiting for a thread that's
+ // already exit.
+ if (android_app->destroyed) {
+ pthread_mutex_unlock(&android_app->mutex);
+ return;
+ }
+
android_app->stateSaved = 0;
android_app_write_cmd(android_app, APP_CMD_SAVE_STATE);
while (!android_app->stateSaved) {
@@ -485,6 +519,15 @@ static bool onTouchEvent(GameActivity* activity,
struct android_app* android_app = ToApp(activity);
pthread_mutex_lock(&android_app->mutex);
+ // NB: we have to consider that the native thread could have already
+ // (gracefully) exit (setting android_app->destroyed) and so we need
+ // to be careful to avoid a deadlock waiting for a thread that's
+ // already exit.
+ if (android_app->destroyed) {
+ pthread_mutex_unlock(&android_app->mutex);
+ return false;
+ }
+
if (android_app->motionEventFilter != NULL &&
!android_app->motionEventFilter(event)) {
pthread_mutex_unlock(&android_app->mutex);
@@ -559,6 +602,15 @@ static bool onKey(GameActivity* activity, const GameActivityKeyEvent* event) {
struct android_app* android_app = ToApp(activity);
pthread_mutex_lock(&android_app->mutex);
+ // NB: we have to consider that the native thread could have already
+ // (gracefully) exit (setting android_app->destroyed) and so we need
+ // to be careful to avoid a deadlock waiting for a thread that's
+ // already exit.
+ if (android_app->destroyed) {
+ pthread_mutex_unlock(&android_app->mutex);
+ return false;
+ }
+
if (android_app->keyEventFilter != NULL &&
!android_app->keyEventFilter(event)) {
pthread_mutex_unlock(&android_app->mutex);
@@ -598,8 +650,9 @@ static void onTextInputEvent(GameActivity* activity,
const GameTextInputState* state) {
struct android_app* android_app = ToApp(activity);
pthread_mutex_lock(&android_app->mutex);
-
- android_app->textInputState = 1;
+ if (!android_app->destroyed) {
+ android_app->textInputState = 1;
+ }
pthread_mutex_unlock(&android_app->mutex);
}
commit 77ef23b4541278922f4b2d1fa376b6a56e6eafb6
Author: Robert Bragg <[email protected]>
Date: Sat Aug 13 05:42:37 2022 +0100
glue: support InputAvailable events
This makes a small change to the C glue code for GameActivity to send
looper wake ups when new input is received (only sending a single wake
up, until the application next handles input).
This makes it possible to recognise that new input is available and send
an `InputAvailable` event to the application - consistent with how
NativeActivity can deliver `InputAvailable` events.
This addresses a significant feature disparity between GameActivity and
NativeActivity that meant GameActivity was not practically usable for
GUI applications that wouldn't want to render continuously like a game.
diff --git a/game-activity/prefab-src/modules/game-activity/include/game-activity/native_app_glue/android_native_app_glue.h b/game-activity/prefab-src/modules/game-activity/include/game-activity/native_app_glue/android_native_app_glue.h
index d5d3eb02d235..f04e434e56ab 100644
--- a/game-activity/prefab-src/modules/game-activity/include/game-activity/native_app_glue/android_native_app_glue.h
+++ b/game-activity/prefab-src/modules/game-activity/include/game-activity/native_app_glue/android_native_app_glue.h
@@ -273,6 +273,26 @@ struct android_app {
android_key_event_filter keyEventFilter;
android_motion_event_filter motionEventFilter;
+ // When new input is received we set both of these flags and use the looper to
+ // wake up the application mainloop.
+ //
+ // To avoid spamming the mainloop with wake ups from lots of input though we
+ // don't sent a wake up if the inputSwapPending flag is already set. (i.e.
+ // we already expect input to be processed in a finite amount of time due to
+ // our previous wake up)
+ //
+ // When a wake up is received then we will check this flag (clearing it
+ // at the same time). If it was set then an InputAvailable event is sent to
+ // the application - which should lead to all input being processed within
+ // a finite amount of time.
+ //
+ // The next time android_app_swap_input_buffers is called, both flags will be
+ // cleared.
+ //
+ // NB: both of these should only be read with the app mutex held
+ bool inputAvailableWakeUp;
+ bool inputSwapPending;
+
/** @endcond */
};
@@ -518,6 +538,11 @@ void android_app_set_motion_event_filter(struct android_app* app,
*/
void android_app_write_cmd(struct android_app* android_app, int8_t cmd);
+/**
+ * Determines if a looper wake up was due to new input becoming available
+ */
+bool android_app_input_available_wake_up(struct android_app* app);
+
#ifdef __cplusplus
}
#endif
diff --git a/game-activity/prefab-src/modules/game-activity/src/game-activity/native_app_glue/android_native_app_glue.c b/game-activity/prefab-src/modules/game-activity/src/game-activity/native_app_glue/android_native_app_glue.c
index f1c135f40d01..745dc03d6de8 100644
--- a/game-activity/prefab-src/modules/game-activity/src/game-activity/native_app_glue/android_native_app_glue.c
+++ b/game-activity/prefab-src/modules/game-activity/src/game-activity/native_app_glue/android_native_app_glue.c
@@ -514,6 +514,29 @@ void android_app_set_motion_event_filter(struct android_app* app,
pthread_mutex_unlock(&app->mutex);
}
+bool android_app_input_available_wake_up(struct android_app* app) {
+ pthread_mutex_lock(&app->mutex);
+ bool available = app->inputAvailableWakeUp;
+ app->inputAvailableWakeUp = false;
+ pthread_mutex_unlock(&app->mutex);
+ return available;
+}
+
+// NB: should be called with the android_app->mutex held already
+static void notifyInput(struct android_app* android_app) {
+ // Don't spam the mainloop with wake ups if we've already sent one
+ if (android_app->inputSwapPending) {
+ return;
+ }
+
+ if (android_app->looper != NULL) {
+ // for the app thread to know why it received the wake() up
+ android_app->inputAvailableWakeUp = true;
+ android_app->inputSwapPending = true;
+ ALooper_wake(android_app->looper);
+ }
+}
+
static bool onTouchEvent(GameActivity* activity,
const GameActivityMotionEvent* event) {
struct android_app* android_app = ToApp(activity);
@@ -554,6 +577,7 @@ static bool onTouchEvent(GameActivity* activity,
memcpy(&inputBuffer->motionEvents[new_ix], event,
sizeof(GameActivityMotionEvent));
++inputBuffer->motionEventsCount;
+ notifyInput(android_app);
android_app_write_cmd(android_app, APP_CMD_TOUCH_EVENT);
pthread_mutex_unlock(&android_app->mutex);
@@ -574,6 +598,9 @@ struct android_input_buffer* android_app_swap_input_buffers(
NATIVE_APP_GLUE_MAX_INPUT_BUFFERS;
}
+ android_app->inputSwapPending = false;
+ android_app->inputAvailableWakeUp = false;
+
pthread_mutex_unlock(&android_app->mutex);
return inputBuffer;
@@ -636,6 +663,7 @@ static bool onKey(GameActivity* activity, const GameActivityKeyEvent* event) {
int new_ix = inputBuffer->keyEventsCount;
memcpy(&inputBuffer->keyEvents[new_ix], event, sizeof(GameActivityKeyEvent));
++inputBuffer->keyEventsCount;
+ notifyInput(android_app);
android_app_write_cmd(android_app, APP_CMD_KEY_EVENT);
pthread_mutex_unlock(&android_app->mutex);
@@ -652,6 +680,7 @@ static void onTextInputEvent(GameActivity* activity,
pthread_mutex_lock(&android_app->mutex);
if (!android_app->destroyed) {
android_app->textInputState = 1;
+ notifyInput(android_app);
}
pthread_mutex_unlock(&android_app->mutex);
}
commit 14243455504385fd2186e0db5913f7f4e3aee17e
Author: Robert Bragg <[email protected]>
Date: Sat Jul 22 18:12:01 2023 +0100
android-activity: rename C symbols that need export
Give C symbols that need to be exported a `_C` suffix so that they can
be linked into a Rust symbol with the correct name (Since we can't
directly export from C/C++ with Rust+Cargo)
See: https://github.com/rust-lang/rfcs/issues/2771
We also rename android_main _rust_glue_entry
The real `android_main` is going to be written in Rust and
android-activity needs to handle its own initialization before calling
the application's `android_main` and so the C/C++ code
calls an intermediate `_rust_glue_entry` function.
diff --git a/game-activity/prefab-src/modules/game-activity/include/game-activity/GameActivity.h b/game-activity/prefab-src/modules/game-activity/include/game-activity/GameActivity.h
index 42f450a1cb5b..b6ff8b44afc5 100644
--- a/game-activity/prefab-src/modules/game-activity/include/game-activity/GameActivity.h
+++ b/game-activity/prefab-src/modules/game-activity/include/game-activity/GameActivity.h
@@ -322,7 +322,7 @@ typedef void GameActivity_createFunc(GameActivity* activity, void* savedState,
* "android.app.func_name" string meta-data in your manifest to use a different
* function.
*/
-extern GameActivity_createFunc GameActivity_onCreate;
+extern GameActivity_createFunc GameActivity_onCreate_C;
/**
* Finish the given activity. Its finish() method will be called, causing it
diff --git a/game-activity/prefab-src/modules/game-activity/include/game-activity/native_app_glue/android_native_app_glue.h b/game-activity/prefab-src/modules/game-activity/include/game-activity/native_app_glue/android_native_app_glue.h
index f04e434e56ab..cdb1ac8d9853 100644
--- a/game-activity/prefab-src/modules/game-activity/include/game-activity/native_app_glue/android_native_app_glue.h
+++ b/game-activity/prefab-src/modules/game-activity/include/game-activity/native_app_glue/android_native_app_glue.h
@@ -501,10 +501,10 @@ void android_app_clear_motion_events(struct android_input_buffer* inputBuffer);
void android_app_clear_key_events(struct android_input_buffer* inputBuffer);
/**
- * This is the function that application code must implement, representing
- * the main entry to the app.
+ * This is a springboard into the Rust glue layer that wraps calling the
+ * main entry for the app itself.
*/
-extern void android_main(struct android_app* app);
+extern void _rust_glue_entry(struct android_app* app);
/**
* Set the filter to use when processing key events.
diff --git a/game-activity/prefab-src/modules/game-activity/src/game-activity/GameActivity.cpp b/game-activity/prefab-src/modules/game-activity/src/game-activity/GameActivity.cpp
index d54f280a4e9e..a178793c9eb0 100644
--- a/game-activity/prefab-src/modules/game-activity/src/game-activity/GameActivity.cpp
+++ b/game-activity/prefab-src/modules/game-activity/src/game-activity/GameActivity.cpp
@@ -538,7 +538,7 @@ static jlong initializeNativeCode_native(
// read configuration for the first time
readConfigurationValues(code, javaConfig);
- GameActivity_onCreate(code, rawSavedState, rawSavedSize);
+ GameActivity_onCreate_C(code, rawSavedState, rawSavedSize);
code->gameTextInput = GameTextInput_init(env, 0);
GameTextInput_setEventCallback(code->gameTextInput,
@@ -1382,8 +1382,12 @@ extern "C" int GameActivity_register(JNIEnv *env) {
NELEM(g_methods));
}
+// XXX: This symbol is renamed with a _C suffix and then re-exported from
+// Rust because Rust/Cargo don't give us a way to directly export symbols
+// from C/C++ code: https://github.com/rust-lang/rfcs/issues/2771
+//
extern "C" JNIEXPORT jlong JNICALL
-Java_com_google_androidgamesdk_GameActivity_initializeNativeCode(
+Java_com_google_androidgamesdk_GameActivity_initializeNativeCode_C(
JNIEnv *env, jobject javaGameActivity, jstring internalDataDir,
jstring obbDir, jstring externalDataDir, jobject jAssetMgr,
jbyteArray savedState, jobject javaConfig) {
diff --git a/game-activity/prefab-src/modules/game-activity/src/game-activity/native_app_glue/android_native_app_glue.c b/game-activity/prefab-src/modules/game-activity/src/game-activity/native_app_glue/android_native_app_glue.c
index 745dc03d6de8..48069d9ce8a3 100644
--- a/game-activity/prefab-src/modules/game-activity/src/game-activity/native_app_glue/android_native_app_glue.c
+++ b/game-activity/prefab-src/modules/game-activity/src/game-activity/native_app_glue/android_native_app_glue.c
@@ -234,7 +234,7 @@ static void* android_app_entry(void* param) {
pthread_cond_broadcast(&android_app->cond);
pthread_mutex_unlock(&android_app->mutex);
- android_main(android_app);
+ _rust_glue_entry(android_app);
android_app_destroy(android_app);
return NULL;
@@ -729,9 +729,13 @@ static bool onEditorAction(GameActivity* activity, int action) {
return true;
}
+// XXX: This symbol is renamed with a _C suffix and then re-exported from
+// Rust because Rust/Cargo don't give us a way to directly export symbols
+// from C/C++ code: https://github.com/rust-lang/rfcs/issues/2771
+//
JNIEXPORT
-void GameActivity_onCreate(GameActivity* activity, void* savedState,
- size_t savedStateSize) {
+void GameActivity_onCreate_C(GameActivity* activity, void* savedState,
+ size_t savedStateSize) {
LOGV("Creating: %p", activity);
activity->callbacks->onDestroy = onDestroy;
activity->callbacks->onStart = onStart;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment