From: Po Lu Date: Wed, 31 May 2023 02:13:04 +0000 (+0800) Subject: Update Android port X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=57903519eb61632c4a85fbaf420109892955079a;p=emacs.git 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. --- 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 . 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. */