[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
feature/android 57903519eb6 1/4: Update Android port
From: |
Po Lu |
Subject: |
feature/android 57903519eb6 1/4: Update Android port |
Date: |
Tue, 30 May 2023 23:30:05 -0400 (EDT) |
branch: feature/android
commit 57903519eb61632c4a85fbaf420109892955079a
Author: Po Lu <luangruo@yahoo.com>
Commit: Po Lu <luangruo@yahoo.com>
Update Android port
* java/debug.sh (is_root): Go back to using unix sockets; allow
adb to forward them correctly.
* java/org/gnu/emacs/EmacsInputConnection.java
(getExtractedText): Don't print text if NULL.
* java/org/gnu/emacs/EmacsService.java (EmacsService): New field
`imSyncInProgress'.
(updateIC): If an IM sync might be in progress, avoid deadlocks.
* java/org/gnu/emacs/EmacsView.java (onCreateInputConnection):
Set `imSyncInProgress' across synchronization point.
* src/android.c (android_check_query): Use __atomic_store_n.
(android_answer_query): New function.
(android_begin_query): Set `android_servicing_query' to 2.
Check once, and don't spin waiting for query to complete.
(android_end_query): Use __atomic_store_n.
(android_run_in_emacs_thread): Compare-and-exchange flag. If
originally 1, fail.
* src/textconv.c (really_set_composing_text): Clear conversion
region if text is empty.
---
java/debug.sh | 17 +++---
java/org/gnu/emacs/EmacsInputConnection.java | 8 +++
java/org/gnu/emacs/EmacsService.java | 28 ++++++++++
java/org/gnu/emacs/EmacsView.java | 8 ++-
src/android.c | 81 ++++++++++++++++++++++------
src/textconv.c | 6 +++
6 files changed, 126 insertions(+), 22 deletions(-)
diff --git a/java/debug.sh b/java/debug.sh
index 339b3604810..0458003fe72 100755
--- a/java/debug.sh
+++ b/java/debug.sh
@@ -19,6 +19,7 @@
## along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
set -m
+set -x
oldpwd=`pwd`
cd `dirname $0`
@@ -310,22 +311,26 @@ rm -f /tmp/file-descriptor-stamp
if [ -z "$gdbserver" ]; then
if [ "$is_root" = "yes" ]; then
- adb -s $device shell $gdbserver_bin --once \
+ adb -s $device shell $gdbserver_bin --multi \
"+/data/local/tmp/debug.$package.socket" --attach $pid >&5 &
gdb_socket="localfilesystem:/data/local/tmp/debug.$package.socket"
- else
- adb -s $device shell run-as $package $gdbserver_bin --once \
+ else
+ adb -s $device shell run-as $package $gdbserver_bin --multi \
"+debug.$package.socket" --attach $pid >&5 &
gdb_socket="localfilesystem:$app_data_dir/debug.$package.socket"
fi
else
# Normally the program cannot access $gdbserver_bin when it is
# placed in /data/local/tmp.
- adb -s $device shell run-as $package $gdbserver_cmd --once \
- "0.0.0.0:7654" --attach $pid >&5 &
- gdb_socket="tcp:7654"
+ adb -s $device shell run-as $package $gdbserver_cmd --multi \
+ "+debug.$package.socket" --attach $pid >&5 &
+ gdb_socket="localfilesystem:$app_data_dir/debug.$package.socket"
fi
+# In order to allow adb to forward to the gdbserver socket, make the
+# app data directory a+x.
+adb -s $device shell run-as $package chmod a+x $app_data_dir
+
# Wait until gdbserver successfully runs.
line=
while read -u 5 line; do
diff --git a/java/org/gnu/emacs/EmacsInputConnection.java
b/java/org/gnu/emacs/EmacsInputConnection.java
index 21bbaca5d07..420da58c0f8 100644
--- a/java/org/gnu/emacs/EmacsInputConnection.java
+++ b/java/org/gnu/emacs/EmacsInputConnection.java
@@ -286,6 +286,14 @@ public final class EmacsInputConnection extends
BaseInputConnection
text = EmacsNative.getExtractedText (windowHandle, request,
flags);
+ if (text == null)
+ {
+ if (EmacsService.DEBUG_IC)
+ Log.d (TAG, "getExtractedText: text is NULL");
+
+ return null;
+ }
+
if (EmacsService.DEBUG_IC)
Log.d (TAG, "getExtractedText: " + text.text + " @"
+ text.startOffset + ":" + text.selectionStart
diff --git a/java/org/gnu/emacs/EmacsService.java
b/java/org/gnu/emacs/EmacsService.java
index 546d22627c5..2f35933a7d1 100644
--- a/java/org/gnu/emacs/EmacsService.java
+++ b/java/org/gnu/emacs/EmacsService.java
@@ -104,6 +104,9 @@ public final class EmacsService extends Service
performing drawing calls. */
private static final boolean DEBUG_THREADS = false;
+ /* Whether or not onCreateInputMethod is calling getSelection. */
+ public static volatile boolean imSyncInProgress;
+
/* Return the directory leading to the directory in which native
library files are stored on behalf of CONTEXT. */
@@ -636,16 +639,41 @@ public final class EmacsService extends Service
int newSelectionEnd, int composingRegionStart,
int composingRegionEnd)
{
+ boolean wasSynchronous;
+
if (DEBUG_IC)
Log.d (TAG, ("updateIC: " + window + " " + newSelectionStart
+ " " + newSelectionEnd + " "
+ composingRegionStart + " "
+ composingRegionEnd));
+
+ /* `updateSelection' holds an internal lock that is also taken
+ before `onCreateInputConnection' (in EmacsView.java) is called;
+ when that then asks the UI thread for the current selection, a
+ dead lock results. To remedy this, reply to any synchronous
+ queries now -- and prohibit more queries for the duration of
+ `updateSelection' -- if EmacsView may have been asking for the
+ value of the region. */
+
+ wasSynchronous = false;
+ if (EmacsService.imSyncInProgress)
+ {
+ /* `beginSynchronous' will answer any outstanding queries and
+ signal that one is now in progress, thereby preventing
+ `getSelection' from blocking. */
+
+ EmacsNative.beginSynchronous ();
+ wasSynchronous = true;
+ }
+
window.view.imManager.updateSelection (window.view,
newSelectionStart,
newSelectionEnd,
composingRegionStart,
composingRegionEnd);
+
+ if (wasSynchronous)
+ EmacsNative.endSynchronous ();
}
public void
diff --git a/java/org/gnu/emacs/EmacsView.java
b/java/org/gnu/emacs/EmacsView.java
index 09bc9d719d3..bb450bb8e6b 100644
--- a/java/org/gnu/emacs/EmacsView.java
+++ b/java/org/gnu/emacs/EmacsView.java
@@ -628,8 +628,14 @@ public final class EmacsView extends ViewGroup
}
/* Obtain the current position of point and set it as the
- selection. */
+ selection. Don't do this under one specific situation: if
+ `android_update_ic' is being called in the main thread, trying
+ to synchronize with it can cause a dead lock in the IM
+ manager. */
+
+ EmacsService.imSyncInProgress = true;
selection = EmacsNative.getSelection (window.handle);
+ EmacsService.imSyncInProgress = false;
if (selection != null)
Log.d (TAG, "onCreateInputConnection: current selection is: "
diff --git a/src/android.c b/src/android.c
index 8cc18787358..9d1399f3fc2 100644
--- a/src/android.c
+++ b/src/android.c
@@ -6959,8 +6959,11 @@ android_display_toast (const char *text)
-/* Whether or not a query is currently being made. */
-static bool android_servicing_query;
+/* The thread from which a query against a thread is currently being
+ made, if any. Value is 0 if no query is in progress, 1 if a query
+ is being made from the UI thread to the main thread, and 2 if a
+ query is being made the other way around. */
+static char android_servicing_query;
/* Function that is waiting to be run in the Emacs thread. */
static void (*android_query_function) (void *);
@@ -7010,7 +7013,37 @@ android_check_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_servicing_query, __ATOMIC_SEQ_CST);
+ __atomic_store_n (&android_servicing_query, 0, __ATOMIC_SEQ_CST);
+
+ /* Signal completion. */
+ sem_post (&android_query_sem);
+}
+
+/* Run the function that the UI thread has asked to run, and then
+ signal its completion. Do not change `android_servicing_query'
+ after it completes. */
+
+static void
+android_answer_query (void)
+{
+ void (*proc) (void *);
+ void *closure;
+
+ eassert (__atomic_load_n (&android_servicing_query, __ATOMIC_SEQ_CST)
+ == 1);
+
+ /* 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. */
+ __atomic_store_n (&android_query_context, NULL, __ATOMIC_SEQ_CST);
+ __atomic_store_n (&android_query_function, NULL, __ATOMIC_SEQ_CST);
/* Signal completion. */
sem_post (&android_query_sem);
@@ -7025,18 +7058,23 @@ android_check_query (void)
static void
android_begin_query (void)
{
- if (__atomic_test_and_set (&android_servicing_query,
- __ATOMIC_SEQ_CST))
+ char old;
+
+ /* Load the previous value of `android_servicing_query' and upgrade
+ it to 2. */
+
+ old = __atomic_exchange_n (&android_servicing_query,
+ 2, __ATOMIC_SEQ_CST);
+
+ /* See if a query was previously in progress. */
+ if (old == 1)
{
/* Answer the query that is currently being made. */
assert (android_query_function != NULL);
- android_check_query ();
-
- /* Wait for that query to complete. */
- while (__atomic_load_n (&android_servicing_query,
- __ATOMIC_SEQ_CST))
- ;;
+ android_answer_query ();
}
+
+ /* `android_servicing_query' is now 2. */
}
/* Notice that a query has stopped. This function may be called from
@@ -7045,7 +7083,7 @@ android_begin_query (void)
static void
android_end_query (void)
{
- __atomic_clear (&android_servicing_query, __ATOMIC_SEQ_CST);
+ __atomic_store_n (&android_servicing_query, 0, __ATOMIC_SEQ_CST);
}
/* Synchronously ask the Emacs thread to run the specified PROC with
@@ -7063,6 +7101,7 @@ int
android_run_in_emacs_thread (void (*proc) (void *), void *closure)
{
union android_event event;
+ char old;
event.xaction.type = ANDROID_WINDOW_ACTION;
event.xaction.serial = ++event_serial;
@@ -7074,10 +7113,13 @@ android_run_in_emacs_thread (void (*proc) (void *),
void *closure)
__atomic_store_n (&android_query_function, proc, __ATOMIC_SEQ_CST);
/* Don't allow deadlocks to happen; make sure the Emacs thread is
- not waiting for something to be done. */
+ not waiting for something to be done (in that case,
+ `android_query_context' is 2.) */
- if (__atomic_test_and_set (&android_servicing_query,
- __ATOMIC_SEQ_CST))
+ old = 0;
+ if (!__atomic_compare_exchange_n (&android_servicing_query, &old,
+ 1, false, __ATOMIC_SEQ_CST,
+ __ATOMIC_SEQ_CST))
{
__atomic_store_n (&android_query_context, NULL,
__ATOMIC_SEQ_CST);
@@ -7098,6 +7140,15 @@ android_run_in_emacs_thread (void (*proc) (void *), void
*closure)
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
+ query. */
+
+ eassert (!__atomic_load_n (&android_servicing_query,
+ __ATOMIC_SEQ_CST)
+ || (__atomic_load_n (&android_servicing_query,
+ __ATOMIC_SEQ_CST) == 2));
+
return 0;
}
diff --git a/src/textconv.c b/src/textconv.c
index 26f351dc729..1530cc0ce32 100644
--- a/src/textconv.c
+++ b/src/textconv.c
@@ -792,6 +792,12 @@ really_set_composing_text (struct frame *f, ptrdiff_t
position,
/* Move the composition overlay. */
sync_overlay (f);
+ /* If TEXT is empty, remove the composing region. This goes against
+ the documentation, but is ultimately what programs expect. */
+
+ if (!SCHARS (text))
+ really_finish_composing_text (f);
+
/* If PT hasn't changed, the conversion region definitely has.
Otherwise, redisplay will update the input method instead. */