1
0
mirror of https://git.savannah.gnu.org/git/emacs.git synced 2025-01-22 18:35:09 +00:00

Simplify code relating to UI thread synchronization

* java/org/gnu/emacs/EmacsContextMenu.java (display):

* java/org/gnu/emacs/EmacsDialog.java (display):

* java/org/gnu/emacs/EmacsService.java (getEmacsView)
(getLocationOnScreen, getClipboardManager)
(requestDirectoryAccess): Replace manual synchronization within
Runnable objects by usage of FutureTask.
(syncRunnable): Accept FutureTask<V> in place of Runnables, and
obtain and return results from calls to its get method.
This commit is contained in:
Po Lu 2023-12-30 10:57:11 +08:00
parent fe2b68d405
commit 94e3d11593
3 changed files with 118 additions and 149 deletions

View File

@ -22,6 +22,9 @@
import java.util.List; import java.util.List;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
@ -344,8 +347,7 @@ private static final class Item implements MenuItem.OnMenuItemClickListener
display (final EmacsWindow window, final int xPosition, display (final EmacsWindow window, final int xPosition,
final int yPosition, final int serial) final int yPosition, final int serial)
{ {
Runnable runnable; FutureTask<Boolean> task;
final EmacsHolder<Boolean> rc;
/* Android will permanently cease to display any popup menus at /* Android will permanently cease to display any popup menus at
all if the list of menu items is empty. Prevent this by all if the list of menu items is empty. Prevent this by
@ -354,25 +356,17 @@ private static final class Item implements MenuItem.OnMenuItemClickListener
if (menuItems.isEmpty ()) if (menuItems.isEmpty ())
return false; return false;
rc = new EmacsHolder<Boolean> (); task = new FutureTask<Boolean> (new Callable<Boolean> () {
rc.thing = false;
runnable = new Runnable () {
@Override @Override
public void public Boolean
run () call ()
{
synchronized (this)
{ {
lastMenuEventSerial = serial; lastMenuEventSerial = serial;
rc.thing = display1 (window, xPosition, yPosition); return display1 (window, xPosition, yPosition);
notify ();
} }
} });
};
EmacsService.syncRunnable (runnable); return EmacsService.<Boolean>syncRunnable (task);
return rc.thing;
} }
/* Dismiss this context menu. WINDOW is the window where the /* Dismiss this context menu. WINDOW is the window where the

View File

@ -22,6 +22,9 @@
import java.util.List; import java.util.List;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.content.Context; import android.content.Context;
@ -388,26 +391,18 @@ else if (EmacsOpenActivity.currentActivity != null)
public boolean public boolean
display () display ()
{ {
Runnable runnable; FutureTask<Boolean> task;
final EmacsHolder<Boolean> rc;
rc = new EmacsHolder<Boolean> (); task = new FutureTask<Boolean> (new Callable<Boolean> () {
rc.thing = false;
runnable = new Runnable () {
@Override @Override
public void public Boolean
run () call ()
{ {
synchronized (this) return display1 ();
{
rc.thing = display1 ();
notify ();
} }
} });
};
EmacsService.syncRunnable (runnable); return EmacsService.<Boolean>syncRunnable (task);
return rc.thing;
} }

View File

@ -27,6 +27,10 @@
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import android.database.Cursor; import android.database.Cursor;
@ -331,52 +335,45 @@ invocation of app_process (through android-emacs) can
final boolean isFocusedByDefault) final boolean isFocusedByDefault)
{ {
Runnable runnable; Runnable runnable;
final EmacsHolder<EmacsView> view; FutureTask<EmacsView> task;
view = new EmacsHolder<EmacsView> (); task = new FutureTask<EmacsView> (new Callable<EmacsView> () {
runnable = new Runnable () {
@Override @Override
public void public EmacsView
run () call ()
{ {
synchronized (this) EmacsView view;
{
view.thing = new EmacsView (window); view = new EmacsView (window);
view.thing.setVisibility (visibility); view.setVisibility (visibility);
/* The following function is only present on Android 26 /* The following function is only present on Android 26
or later. */ or later. */
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
view.thing.setFocusedByDefault (isFocusedByDefault); view.setFocusedByDefault (isFocusedByDefault);
notify (); return view;
} }
} });
};
syncRunnable (runnable); return EmacsService.<EmacsView>syncRunnable (task);
return view.thing;
} }
public void public void
getLocationOnScreen (final EmacsView view, final int[] coordinates) getLocationOnScreen (final EmacsView view, final int[] coordinates)
{ {
Runnable runnable; FutureTask<Void> task;
runnable = new Runnable () { task = new FutureTask<Void> (new Callable<Void> () {
public void public Void
run () call ()
{
synchronized (this)
{ {
view.getLocationOnScreen (coordinates); view.getLocationOnScreen (coordinates);
notify (); return null;
} }
} });
};
syncRunnable (runnable); EmacsService.<Void>syncRunnable (task);
} }
@ -702,28 +699,17 @@ invocation of app_process (through android-emacs) can
public ClipboardManager public ClipboardManager
getClipboardManager () getClipboardManager ()
{ {
final EmacsHolder<ClipboardManager> manager; FutureTask<Object> task;
Runnable runnable;
manager = new EmacsHolder<ClipboardManager> (); task = new FutureTask<Object> (new Callable<Object> () {
public Object
runnable = new Runnable () { call ()
public void
run ()
{ {
Object tem; return getSystemService (Context.CLIPBOARD_SERVICE);
synchronized (this)
{
tem = getSystemService (Context.CLIPBOARD_SERVICE);
manager.thing = (ClipboardManager) tem;
notify ();
} }
} });
};
syncRunnable (runnable); return (ClipboardManager) EmacsService.<Object>syncRunnable (task);
return manager.thing;
} }
public void public void
@ -738,33 +724,37 @@ invocation of app_process (through android-emacs) can
System.exit (0); System.exit (0);
} }
/* Wait synchronously for the specified RUNNABLE to complete in the /* Wait synchronously for the specified TASK to complete in the UI
UI thread. Must be called from the Emacs thread. */ thread, then return its result. Must be called from the Emacs
thread. */
public static void public static <V> V
syncRunnable (Runnable runnable) syncRunnable (FutureTask<V> task)
{ {
V object;
EmacsNative.beginSynchronous (); EmacsNative.beginSynchronous ();
SERVICE.runOnUiThread (task);
synchronized (runnable)
{
SERVICE.runOnUiThread (runnable);
while (true)
{
try try
{ {
runnable.wait (); object = task.get ();
break;
} }
catch (InterruptedException e) catch (ExecutionException exception)
{ {
continue; /* Wrap this exception in a RuntimeException and signal it to
} the caller. */
throw new RuntimeException (exception.getCause ());
} }
catch (InterruptedException exception)
{
EmacsNative.emacsAbort ();
object = null;
} }
EmacsNative.endSynchronous (); EmacsNative.endSynchronous ();
return object;
} }
@ -1283,71 +1273,61 @@ else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
public int public int
requestDirectoryAccess () requestDirectoryAccess ()
{ {
Runnable runnable; FutureTask<Integer> task;
final EmacsHolder<Integer> rc;
/* Return 1 if Android is too old to support this feature. */ /* Return 1 if Android is too old to support this feature. */
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
return 1; return 1;
rc = new EmacsHolder<Integer> (); task = new FutureTask<Integer> (new Callable<Integer> () {
rc.thing = Integer.valueOf (1);
runnable = new Runnable () {
@Override @Override
public void public Integer
run () call ()
{ {
EmacsActivity activity; EmacsActivity activity;
Intent intent; Intent intent;
int id; int id, rc;
synchronized (this) /* Try to obtain an activity that will receive the response
{ from the file chooser dialog. */
/* Try to obtain an activity that will receive the
response from the file chooser dialog. */
if (EmacsActivity.focusedActivities.isEmpty ()) if (EmacsActivity.focusedActivities.isEmpty ())
{ {
/* If focusedActivities is empty then this dialog /* If focusedActivities is empty then this dialog may
may have been displayed immediately after another have been displayed immediately after another popup
popup dialog was dismissed. Try the dialog was dismissed. Try the EmacsActivity to be
EmacsActivity to be focused. */ focused. */
activity = EmacsActivity.lastFocusedActivity; activity = EmacsActivity.lastFocusedActivity;
if (activity == null) if (activity == null)
{
/* Still no luck. Return failure. */ /* Still no luck. Return failure. */
notify (); return 1;
return;
}
} }
else else
activity = EmacsActivity.focusedActivities.get (0); activity = EmacsActivity.focusedActivities.get (0);
/* Now create the intent. */ /* Now create the intent. */
intent = new Intent (Intent.ACTION_OPEN_DOCUMENT_TREE); intent = new Intent (Intent.ACTION_OPEN_DOCUMENT_TREE);
rc = 1;
try try
{ {
id = EmacsActivity.ACCEPT_DOCUMENT_TREE; id = EmacsActivity.ACCEPT_DOCUMENT_TREE;
activity.startActivityForResult (intent, id, null); activity.startActivityForResult (intent, id, null);
rc.thing = Integer.valueOf (0); rc = 0;
} }
catch (Exception e) catch (Exception e)
{ {
e.printStackTrace (); e.printStackTrace ();
} }
notify (); return rc;
} }
} });
};
syncRunnable (runnable); return EmacsService.<Integer>syncRunnable (task);
return rc.thing;
} }
/* Return an array of each tree provided by the document PROVIDER /* Return an array of each tree provided by the document PROVIDER