Compare commits

..

3 Commits

Author SHA1 Message Date
Robert Bragg 14a1b0a846 Merge tag 'v0.4.2' into v0.4 2023-06-27 17:48:20 +01:00
Robert Bragg e16b5b82d9 Merge commit '36ddfaa9cea7af9377aa43e0db6cc56e80ccc234' into v0.4 2023-06-27 17:47:32 +01:00
Robert Bragg 79d0a07564 Merge branch 'release-0.4' into v0.4 2022-11-14 16:15:44 +00:00
5 changed files with 14 additions and 110 deletions
+1 -8
View File
@@ -1,18 +1,11 @@
<!-- markdownlint-disable MD022 MD024 MD032 -->
# Changelog # Changelog
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased] ## [Unreleased]
## [0.4.3] - 2022-07-30
### Fixed
- Fixed a deadlock in the `native-activity` backend while waiting for the native thread after getting an `onDestroy` callback from Java ([#94](https://github.com/rust-mobile/android-activity/pull/94))
- Fixed numerous deadlocks in the `game-activity` backend with how it would wait for the native thread in various Java callbacks, after the app has returned from `android_main` ([#98](https://github.com/rust-mobile/android-activity/pull/98)) ## [0.4.2] - 2022-02-16
## [0.4.2] - 2022-06-17
### Changed ### Changed
- The `Activity.finish()` method is now called when `android_main` returns so the `Activity` will be destroyed ([#67](https://github.com/rust-mobile/android-activity/issues/67)) - The `Activity.finish()` method is now called when `android_main` returns so the `Activity` will be destroyed ([#67](https://github.com/rust-mobile/android-activity/issues/67))
- The `native-activity` backend now propagates `NativeWindow` redraw/resize and `ContentRectChanged` callbacks to main loop ([#70](https://github.com/rust-mobile/android-activity/pull/70)) - The `native-activity` backend now propagates `NativeWindow` redraw/resize and `ContentRectChanged` callbacks to main loop ([#70](https://github.com/rust-mobile/android-activity/pull/70))
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "android-activity" name = "android-activity"
version = "0.4.3" version = "0.4.2"
edition = "2021" edition = "2021"
keywords = ["android", "ndk"] keywords = ["android", "ndk"]
readme = "../README.md" readme = "../README.md"
-11
View File
@@ -1,9 +1,6 @@
#![allow(dead_code)] #![allow(dead_code)]
fn build_glue_for_game_activity() { fn build_glue_for_game_activity() {
for f in ["GameActivity.h", "GameActivity.cpp"] {
println!("cargo:rerun-if-changed=game-activity-csrc/game-activity/{f}");
}
cc::Build::new() cc::Build::new()
.cpp(true) .cpp(true)
.include("game-activity-csrc") .include("game-activity-csrc")
@@ -11,20 +8,12 @@ fn build_glue_for_game_activity() {
.extra_warnings(false) .extra_warnings(false)
.cpp_link_stdlib("c++_static") .cpp_link_stdlib("c++_static")
.compile("libgame_activity.a"); .compile("libgame_activity.a");
for f in ["gamecommon.h", "gametextinput.h", "gametextinput.cpp"] {
println!("cargo:rerun-if-changed=game-activity-csrc/game-text-input/{f}");
}
cc::Build::new() cc::Build::new()
.cpp(true) .cpp(true)
.include("game-activity-csrc") .include("game-activity-csrc")
.file("game-activity-csrc/game-text-input/gametextinput.cpp") .file("game-activity-csrc/game-text-input/gametextinput.cpp")
.cpp_link_stdlib("c++_static") .cpp_link_stdlib("c++_static")
.compile("libgame_text_input.a"); .compile("libgame_text_input.a");
for f in ["android_native_app_glue.h", "android_native_app_glue.c"] {
println!("cargo:rerun-if-changed=game-activity-csrc/native_app_glue/{f}");
}
cc::Build::new() cc::Build::new()
.include("game-activity-csrc") .include("game-activity-csrc")
.include("game-activity-csrc/game-activity/native_app_glue") .include("game-activity-csrc/game-activity/native_app_glue")
@@ -308,15 +308,6 @@ static void android_app_set_window(struct android_app* android_app,
ANativeWindow* window) { ANativeWindow* window) {
LOGV("android_app_set_window called"); LOGV("android_app_set_window called");
pthread_mutex_lock(&android_app->mutex); 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) { if (android_app->pendingWindow != NULL) {
android_app_write_cmd(android_app, APP_CMD_TERM_WINDOW); android_app_write_cmd(android_app, APP_CMD_TERM_WINDOW);
} }
@@ -333,30 +324,15 @@ static void android_app_set_window(struct android_app* android_app,
static void android_app_set_activity_state(struct android_app* android_app, static void android_app_set_activity_state(struct android_app* android_app,
int8_t cmd) { int8_t cmd) {
pthread_mutex_lock(&android_app->mutex); pthread_mutex_lock(&android_app->mutex);
android_app_write_cmd(android_app, cmd);
// NB: we have to consider that the native thread could have already while (android_app->activityState != cmd) {
// (gracefully) exit (setting android_app->destroyed) and so we need pthread_cond_wait(&android_app->cond, &android_app->mutex);
// 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); pthread_mutex_unlock(&android_app->mutex);
} }
static void android_app_free(struct android_app* android_app) { static void android_app_free(struct android_app* android_app) {
pthread_mutex_lock(&android_app->mutex); 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); android_app_write_cmd(android_app, APP_CMD_DESTROY);
while (!android_app->destroyed) { while (!android_app->destroyed) {
pthread_cond_wait(&android_app->cond, &android_app->mutex); pthread_cond_wait(&android_app->cond, &android_app->mutex);
@@ -396,16 +372,6 @@ static void onSaveInstanceState(GameActivity* activity,
struct android_app* android_app = ToApp(activity); struct android_app* android_app = ToApp(activity);
pthread_mutex_lock(&android_app->mutex); 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->stateSaved = 0;
android_app_write_cmd(android_app, APP_CMD_SAVE_STATE); android_app_write_cmd(android_app, APP_CMD_SAVE_STATE);
while (!android_app->stateSaved) { while (!android_app->stateSaved) {
@@ -515,15 +481,6 @@ static bool onTouchEvent(GameActivity* activity,
struct android_app* android_app = ToApp(activity); struct android_app* android_app = ToApp(activity);
pthread_mutex_lock(&android_app->mutex); 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 && if (android_app->motionEventFilter != NULL &&
!android_app->motionEventFilter(event)) { !android_app->motionEventFilter(event)) {
pthread_mutex_unlock(&android_app->mutex); pthread_mutex_unlock(&android_app->mutex);
@@ -606,15 +563,6 @@ static bool onKey(GameActivity* activity, const GameActivityKeyEvent* event) {
struct android_app* android_app = ToApp(activity); struct android_app* android_app = ToApp(activity);
pthread_mutex_lock(&android_app->mutex); 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 && if (android_app->keyEventFilter != NULL &&
!android_app->keyEventFilter(event)) { !android_app->keyEventFilter(event)) {
pthread_mutex_unlock(&android_app->mutex); pthread_mutex_unlock(&android_app->mutex);
@@ -651,9 +599,8 @@ static void onTextInputEvent(GameActivity* activity,
const GameTextInputState* state) { const GameTextInputState* state) {
struct android_app* android_app = ToApp(activity); struct android_app* android_app = ToApp(activity);
pthread_mutex_lock(&android_app->mutex); pthread_mutex_lock(&android_app->mutex);
if (!android_app->destroyed) {
android_app->textInputState = 1; android_app->textInputState = 1;
}
pthread_mutex_unlock(&android_app->mutex); pthread_mutex_unlock(&android_app->mutex);
} }
+7 -32
View File
@@ -199,18 +199,6 @@ impl NativeActivityGlue {
} }
} }
/// The status of the native thread that's created to run
/// `android_main`
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum NativeThreadState {
/// The `android_main` thread hasn't been created yet
Init,
/// The `android_main` thread has been spawned and started running
Running,
/// The `android_main` thread has finished
Stopped,
}
#[derive(Debug)] #[derive(Debug)]
pub struct NativeActivityState { pub struct NativeActivityState {
pub msg_read: libc::c_int, pub msg_read: libc::c_int,
@@ -222,11 +210,8 @@ pub struct NativeActivityState {
pub content_rect: ndk_sys::ARect, pub content_rect: ndk_sys::ARect,
pub activity_state: State, pub activity_state: State,
pub destroy_requested: bool, pub destroy_requested: bool,
pub thread_state: NativeThreadState, pub running: bool,
pub app_has_saved_state: bool, pub app_has_saved_state: bool,
/// Set as soon as the Java main thread notifies us of an
/// `onDestroyed` callback.
pub destroyed: bool, pub destroyed: bool,
pub redraw_needed: bool, pub redraw_needed: bool,
pub pending_input_queue: *mut ndk_sys::AInputQueue, pub pending_input_queue: *mut ndk_sys::AInputQueue,
@@ -318,6 +303,8 @@ impl Drop for WaitableNativeActivityState {
unsafe { unsafe {
let mut guard = self.mutex.lock().unwrap(); let mut guard = self.mutex.lock().unwrap();
guard.detach_input_queue_from_looper(); guard.detach_input_queue_from_looper();
guard.destroyed = true;
self.cond.notify_one();
} }
} }
} }
@@ -369,7 +356,7 @@ impl WaitableNativeActivityState {
content_rect: Rect::empty().into(), content_rect: Rect::empty().into(),
activity_state: State::Init, activity_state: State::Init,
destroy_requested: false, destroy_requested: false,
thread_state: NativeThreadState::Init, running: false,
app_has_saved_state: false, app_has_saved_state: false,
destroyed: false, destroyed: false,
redraw_needed: false, redraw_needed: false,
@@ -382,11 +369,10 @@ impl WaitableNativeActivityState {
pub fn notify_destroyed(&self) { pub fn notify_destroyed(&self) {
let mut guard = self.mutex.lock().unwrap(); let mut guard = self.mutex.lock().unwrap();
guard.destroyed = true;
unsafe { unsafe {
guard.write_cmd(AppCmd::Destroy); guard.write_cmd(AppCmd::Destroy);
while guard.thread_state != NativeThreadState::Stopped { while !guard.destroyed {
guard = self.cond.wait(guard).unwrap(); guard = self.cond.wait(guard).unwrap();
} }
@@ -555,13 +541,7 @@ impl WaitableNativeActivityState {
pub fn notify_main_thread_running(&self) { pub fn notify_main_thread_running(&self) {
let mut guard = self.mutex.lock().unwrap(); let mut guard = self.mutex.lock().unwrap();
guard.thread_state = NativeThreadState::Running; guard.running = true;
self.cond.notify_one();
}
pub fn notify_main_thread_stopped_running(&self) {
let mut guard = self.mutex.lock().unwrap();
guard.thread_state = NativeThreadState::Stopped;
self.cond.notify_one(); self.cond.notify_one();
} }
@@ -927,16 +907,11 @@ extern "C" fn ANativeActivity_onCreate(
ndk_context::release_android_context(); ndk_context::release_android_context();
} }
rust_glue.notify_main_thread_stopped_running();
}); });
// Wait for thread to start. // Wait for thread to start.
let mut guard = jvm_glue.mutex.lock().unwrap(); let mut guard = jvm_glue.mutex.lock().unwrap();
while !guard.running {
// Don't specifically wait for `Running` just in case `android_main` returns
// immediately and the state is set to `Stopped`
while guard.thread_state == NativeThreadState::Init {
guard = jvm_glue.cond.wait(guard).unwrap(); guard = jvm_glue.cond.wait(guard).unwrap();
} }
}) })