[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
feature/android 674373bed86 1/2: Prevent hangs from IM requests with the
From: |
Po Lu |
Subject: |
feature/android 674373bed86 1/2: Prevent hangs from IM requests with the main thread busy |
Date: |
Fri, 9 Jun 2023 21:25:28 -0400 (EDT) |
branch: feature/android
commit 674373bed8632093ab8ed118618b05e085ffd5cd
Author: Po Lu <luangruo@yahoo.com>
Commit: Po Lu <luangruo@yahoo.com>
Prevent hangs from IM requests with the main thread busy
* src/android.c (android_select): Clear `android_urgent_query'.
(android_check_query): Make static. Clear
`android_urgent_query'.
(android_check_query_urgent): New function; work like
`android_check_query', but only answer urgent queries.
(android_answer_query, android_end_query): Clear urgent query
flag.
(android_run_in_emacs_thread): Initially wait two seconds for
the query to run from the keyboard loop; upon a timeout, set
`android_urgent_query' to true and wait for it to run while
reading async input.
* src/android.h: Update prototypes.
* src/keyboard.c (handle_async_input): Call
`android_check_query_urgent'.
---
src/android.c | 113 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--
src/android.h | 2 +-
src/keyboard.c | 14 ++++++-
3 files changed, 123 insertions(+), 6 deletions(-)
diff --git a/src/android.c b/src/android.c
index f45e0abbea6..cfc777c3caa 100644
--- a/src/android.c
+++ b/src/android.c
@@ -29,6 +29,7 @@ along with GNU Emacs. If not, see
<https://www.gnu.org/licenses/>. */
#include <math.h>
#include <string.h>
#include <stdckdint.h>
+#include <timespec.h>
#include <sys/stat.h>
#include <sys/mman.h>
@@ -693,6 +694,17 @@ android_write_event (union android_event *event)
}
}
+
+
+/* Whether or not the UI thread has been waiting for a significant
+ amount of time for a function to run in the main thread, and Emacs
+ should answer the query ASAP. */
+static bool android_urgent_query;
+
+/* Forward declaration. */
+
+static void android_check_query (void);
+
int
android_select (int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timespec *timeout)
@@ -702,6 +714,11 @@ android_select (int nfds, fd_set *readfds, fd_set
*writefds,
static char byte;
#endif
+ /* Since Emacs is reading keyboard input again, signify that queries
+ from input methods are no longer ``urgent''. */
+
+ __atomic_clear (&android_urgent_query, __ATOMIC_SEQ_CST);
+
/* Check for and run anything the UI thread wants to run on the main
thread. */
android_check_query ();
@@ -7066,7 +7083,7 @@ static void *android_query_context;
/* Run any function that the UI thread has asked to run, and then
signal its completion. */
-void
+static void
android_check_query (void)
{
void (*proc) (void *);
@@ -7088,6 +7105,49 @@ android_check_query (void)
__atomic_store_n (&android_query_context, NULL, __ATOMIC_SEQ_CST);
__atomic_store_n (&android_query_function, NULL, __ATOMIC_SEQ_CST);
__atomic_store_n (&android_servicing_query, 0, __ATOMIC_SEQ_CST);
+ __atomic_clear (&android_urgent_query, __ATOMIC_SEQ_CST);
+
+ /* Signal completion. */
+ sem_post (&android_query_sem);
+}
+
+/* Run any function that the UI thread has asked to run, if the UI
+ thread has been waiting for more than two seconds.
+
+ Call this from `process_pending_signals' to ensure that the UI
+ thread always receives an answer within a reasonable amount of
+ time. */
+
+void
+android_check_query_urgent (void)
+{
+ void (*proc) (void *);
+ void *closure;
+
+ if (!__atomic_load_n (&android_urgent_query, __ATOMIC_SEQ_CST))
+ return;
+
+ __android_log_print (ANDROID_LOG_VERBOSE, __func__,
+ "Responding to urgent query...");
+
+ if (!__atomic_load_n (&android_servicing_query, __ATOMIC_SEQ_CST))
+ return;
+
+ /* First, load the procedure and closure. */
+ __atomic_load (&android_query_context, &closure, __ATOMIC_SEQ_CST);
+ __atomic_load (&android_query_function, &proc, __ATOMIC_SEQ_CST);
+
+ if (!proc)
+ return;
+
+ proc (closure);
+
+ /* Finish the query. Don't clear `android_urgent_query'; instead,
+ do that the next time Emacs enters the keyboard loop. */
+
+ __atomic_store_n (&android_query_context, NULL, __ATOMIC_SEQ_CST);
+ __atomic_store_n (&android_query_function, NULL, __ATOMIC_SEQ_CST);
+ __atomic_store_n (&android_servicing_query, 0, __ATOMIC_SEQ_CST);
/* Signal completion. */
sem_post (&android_query_sem);
@@ -7118,6 +7178,7 @@ android_answer_query (void)
/* Finish the query. */
__atomic_store_n (&android_query_context, NULL, __ATOMIC_SEQ_CST);
__atomic_store_n (&android_query_function, NULL, __ATOMIC_SEQ_CST);
+ __atomic_clear (&android_urgent_query, __ATOMIC_SEQ_CST);
/* Signal completion. */
sem_post (&android_query_sem);
@@ -7175,6 +7236,7 @@ static void
android_end_query (void)
{
__atomic_store_n (&android_servicing_query, 0, __ATOMIC_SEQ_CST);
+ __atomic_clear (&android_urgent_query, __ATOMIC_SEQ_CST);
}
/* Synchronously ask the Emacs thread to run the specified PROC with
@@ -7193,6 +7255,8 @@ android_run_in_emacs_thread (void (*proc) (void *), void
*closure)
{
union android_event event;
char old;
+ int rc;
+ struct timespec timeout;
event.xaction.type = ANDROID_WINDOW_ACTION;
event.xaction.serial = ++event_serial;
@@ -7227,9 +7291,50 @@ android_run_in_emacs_thread (void (*proc) (void *), void
*closure)
time it is entered. */
android_write_event (&event);
- /* Start waiting for the function to be executed. */
- while (sem_wait (&android_query_sem) < 0)
- ;;
+ /* Start waiting for the function to be executed. First, wait two
+ seconds for the query to execute normally. */
+
+ timeout.tv_sec = 2;
+ timeout.tv_nsec = 0;
+ timeout = timespec_add (current_timespec (), timeout);
+
+ /* See if an urgent query was recently answered without entering the
+ keyboard loop in between. When that happens, raise SIGIO to
+ continue processing queries as soon as possible. */
+
+ if (__atomic_load_n (&android_urgent_query, __ATOMIC_SEQ_CST))
+ raise (SIGIO);
+
+ again:
+ rc = sem_timedwait (&android_query_sem, &timeout);
+
+ if (rc < 0)
+ {
+ if (errno == EINTR)
+ goto again;
+
+ eassert (errno == ETIMEDOUT);
+
+ __android_log_print (ANDROID_LOG_VERBOSE, __func__,
+ "Timed out waiting for response"
+ " from main thread...");
+
+ /* The query timed out. At this point, set
+ `android_urgent_query' to true. */
+ __atomic_store_n (&android_urgent_query, true, __ATOMIC_SEQ_CST);
+
+ /* And raise SIGIO. Now that the query is considered urgent,
+ the main thread will reply while reading async input.
+
+ Normally, the main thread waits for the keyboard loop to be
+ entered before responding, in order to avoid responding with
+ inaccurate results taken during command executioon. */
+ raise (SIGIO);
+
+ /* Wait for the query to complete. */
+ while (sem_wait (&android_query_sem) < 0)
+ ;;
+ }
/* At this point, `android_servicing_query' should either be zero if
the query was answered or two if the main thread has started a
diff --git a/src/android.h b/src/android.h
index c748d99a09a..8634ba01a3d 100644
--- a/src/android.h
+++ b/src/android.h
@@ -185,7 +185,7 @@ extern void android_display_toast (const char *);
/* Event loop functions. */
-extern void android_check_query (void);
+extern void android_check_query_urgent (void);
extern int android_run_in_emacs_thread (void (*) (void *), void *);
extern void android_write_event (union android_event *);
diff --git a/src/keyboard.c b/src/keyboard.c
index f31f717195b..523718cdbaa 100644
--- a/src/keyboard.c
+++ b/src/keyboard.c
@@ -47,7 +47,11 @@ along with GNU Emacs. If not, see
<https://www.gnu.org/licenses/>. */
#ifdef HAVE_TEXT_CONVERSION
#include "textconv.h"
-#endif
+#endif /* HAVE_TEXT_CONVERSION */
+
+#ifdef HAVE_ANDROID
+#include "android.h"
+#endif /* HAVE_ANDROID */
#include <errno.h>
@@ -7906,6 +7910,14 @@ tty_read_avail_input (struct terminal *terminal,
static void
handle_async_input (void)
{
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+ /* Check and respond to an ``urgent'' query from the UI thread.
+ A query becomes urgent once the UI thread has been waiting
+ for more than two seconds. */
+
+ android_check_query_urgent ();
+#endif /* HAVE_ANDROID && !ANDROID_STUBIFY */
+
#ifndef DOS_NT
while (1)
{