emacs-diffs
[Top][All Lists]
Advanced

[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.  */
 



reply via email to

[Prev in Thread] Current Thread [Next in Thread]