Much like the NS port, only the main thread receives input from
the user interface, which is fortunately not a major problem for
packages such as lsp-mode that create Lisp threads.
* configure.ac: Enable with_threads under Android.
* src/android.c (android_init_events): Set `main_thread_id' to
the ID of the main thread.
(setEmacsParams): Set new global variable `android_jvm' to the
JVM object, for the purpose of attaching Lisp threads to the
JVM.
(android_select): [THREADS_ENABLED]: If the caller isn't the
main thread, resort to pselect. Don't check query before select
returns.
(android_check_query): Export.
* src/android.h (_ANDROID_H_): Define new macro and update
prototypes.
* src/process.c (android_select_wrapper): New function.
(wait_reading_process_output): If THREADS_ENABLED, call
thread_select through the Android select wrapper.
* src/thread.c (post_acquire_global_lock): Call
android_check_query; replace android_java_env with the incoming
Lisp thread's.
(run_thread): Attach and detach the thread created to the JVM.
(init_threads): Set the main thread's JNI environment object.
* src/thread.h (struct thread_state) <java_env>: New field.
(cherry picked from commit
42db7292c3e05920bc9f2fa5c3478eb2ba835c5c)
passthrough="$passthrough --with-mailutils=$with_mailutils"
passthrough="$passthrough --with-pop=$with_pop"
passthrough="$passthrough --with-harfbuzz=$with_harfbuzz"
+ passthrough="$passthrough --with-threads=$with_png"
# Now pass through some checking options.
emacs_val="--enable-check-lisp-object-type=$enable_check_lisp_object_type"
with_pop=no
with_harfbuzz=no
with_native_compilation=no
+ with_threads=no
fi
with_rsvg=no
with_gpm=no
with_dbus=no
with_gsettings=no
- with_threads=no
with_ns=no
# zlib is available in android.
#include <sys/param.h>
#include <sys/stat.h>
+#include <sys/select.h>
/* Old NDK versions lack MIN and MAX. */
#include <minmax.h>
/* The Java environment being used for the main thread. */
JNIEnv *android_java_env;
+#ifdef THREADS_ENABLED
+
+/* The Java VM new threads attach to. */
+JavaVM *android_jvm;
+
+#endif /* THREADS_ENABLED */
+
/* The EmacsGC class. */
static jclass emacs_gc_class;
This should ideally be defined further down. */
static sem_t android_query_sem;
+/* ID of the Emacs thread. */
+static pthread_t main_thread_id;
+
/* Set up the global event queue by initializing the mutex and two
condition variables, and the linked list of events. This must be
called before starting the Emacs thread. Also, initialize the
event_queue.events.next = &event_queue.events;
event_queue.events.last = &event_queue.events;
+ main_thread_id = pthread_self ();
+
#if __ANDROID_API__ >= 16
/* Before starting the select thread, make sure the disposition for
return i;
}
-/* Forward declaration. */
-
-static void android_check_query (void);
-
/* Wait for events to become available synchronously. Return once an
event arrives. Also, reply to the UI thread whenever it requires a
response. */
static char byte;
#endif
+#ifdef THREADS_ENABLED
+ if (!pthread_equal (pthread_self (), main_thread_id))
+ return pselect (nfds, readfds, writefds, exceptfds, timeout,
+ NULL);
+#endif /* THREADS_ENABLED */
+
/* Since Emacs is reading keyboard input again, signify that queries
from input methods are no longer ``urgent''. */
if (nfds_return < 0)
errno = EINTR;
+#ifndef THREADS_ENABLED
/* Now check for and run anything the UI thread wants to run in the
main thread. */
android_check_query ();
+#endif /* THREADS_ENABLED */
return nfds_return;
}
const char *java_string;
struct stat statb;
+#ifdef THREADS_ENABLED
+ /* Save the Java VM. */
+ if ((*env)->GetJavaVM (env, &android_jvm))
+ emacs_abort ();
+#endif /* THREADS_ENABLED */
+
/* Set the Android API level early, as it is used by
`android_vfs_init'. */
android_api_level = api_level;
/* This function should only be called from the main thread. */
-
android_pixel_density_x = pixel_density_x;
android_pixel_density_y = pixel_density_y;
android_scaled_pixel_density = scaled_density;
/* Run any function that the UI thread has asked to run, and then
signal its completion. */
-static void
+void
android_check_query (void)
{
void (*proc) (void *);
a table of function pointers. */
#ifndef _ANDROID_H_
+#define _ANDROID_H_
+
#ifndef ANDROID_STUBIFY
#include <jni.h>
#include <pwd.h>
/* 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 *);
extern JNIEnv *android_java_env;
+#ifdef THREADS_ENABLED
+extern JavaVM *android_jvm;
+#endif /* THREADS_ENABLED */
+
/* The EmacsService object. */
extern jobject emacs_service;
{
}
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY \
+ && defined THREADS_ENABLED
+
+/* Wrapper around `android_select' that exposes a calling interface with
+ an extra argument for compatibility with `thread_pselect'. */
+
+static int
+android_select_wrapper (int nfds, fd_set *readfds, fd_set *writefds,
+ fd_set *exceptfds, const struct timespec *timeout,
+ const sigset_t *sigmask)
+{
+ /* sigmask is not supported. */
+ if (sigmask)
+ emacs_abort ();
+
+ return android_select (nfds, readfds, writefds, exceptfds,
+ (struct timespec *) timeout);
+}
+
+#endif /* HAVE_ANDROID && !ANDROID_STUBIFY && THREADS_ENABLED */
+
/* Read and dispose of subprocess output while waiting for timeout to
elapse and/or keyboard input to be available.
timeout = short_timeout;
#endif
- /* Android doesn't support threads and requires using a
- replacement for pselect in android.c to poll for
- events. */
+ /* Android requires using a replacement for pselect in
+ android.c to poll for events. */
#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+#ifndef THREADS_ENABLED
nfds = android_select (max_desc + 1,
&Available, (check_write ? &Writeok : 0),
NULL, &timeout);
+#else /* THREADS_ENABLED */
+ nfds = thread_select (android_select_wrapper,
+ max_desc + 1,
+ &Available, (check_write ? &Writeok : 0),
+ NULL, &timeout, NULL);
+#endif /* THREADS_ENABLED */
#else
/* Non-macOS HAVE_GLIB builds call thread_select in
{
struct thread_state *prev_thread = current_thread;
+ /* Switch the JNI interface pointer to the environment assigned to the
+ current thread. */
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+ android_java_env = self->java_env;
+#endif /* defined HAVE_ANDROID && !defined ANDROID_STUBIFY */
+
/* Do this early on, so that code below could signal errors (e.g.,
unbind_for_thread_switch might) correctly, because we are already
running in the context of the thread pointed by SELF. */
set_buffer_internal_2 (current_buffer);
}
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+ /* This step is performed in android_select when built without
+ threads. */
+ android_check_query ();
+#endif /* defined HAVE_ANDROID && !defined ANDROID_STUBIFY */
+
/* We could have been signaled while waiting to grab the global lock
for the first time since this thread was created, in which case
we didn't yet have the opportunity to set up the handlers. Delay
struct thread_state *self = state;
struct thread_state **iter;
+#ifdef THREADS_ENABLED
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+ jint rc;
+#endif /* #if defined HAVE_ANDROID && !defined ANDROID_STUBIFY */
+#endif /* THREADS_ENABLED */
#ifdef HAVE_NS
/* Allocate an autorelease pool in case this thread calls any
void *pool = ns_alloc_autorelease_pool ();
#endif
+#ifdef THREADS_ENABLED
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+ rc
+ = (*android_jvm)->AttachCurrentThread (android_jvm, &self->java_env,
+ NULL);
+ if (rc != JNI_OK)
+ emacs_abort ();
+#endif /* defined HAVE_ANDROID && !defined ANDROID_STUBIFY */
+#endif /* THREADS_ENABLED */
+
self->m_stack_bottom = self->stack_top = &stack_pos.c;
self->thread_id = sys_thread_self ();
ns_release_autorelease_pool (pool);
#endif
+#ifdef THREADS_ENABLED
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+ rc = (*android_jvm)->DetachCurrentThread (android_jvm);
+ if (rc != JNI_OK)
+ emacs_abort ();
+#endif /* defined HAVE_ANDROID && !defined ANDROID_STUBIFY */
+#endif /* THREADS_ENABLED */
+
/* Unlink this thread from the list of all threads. Note that we
have to do this very late, after broadcasting our death.
Otherwise the GC may decide to reap the thread_state object,
sys_mutex_init (&global_lock);
sys_mutex_lock (&global_lock);
current_thread = &main_thread.s;
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+ current_thread->java_env = android_java_env;
+#endif /* defined HAVE_ANDROID && !defined ANDROID_STUBIFY */
+
main_thread.s.thread_id = sys_thread_self ();
init_bc_thread (&main_thread.s.bc);
}
#include <signal.h> /* sigset_t */
#endif
+#ifdef HAVE_ANDROID
+#ifndef ANDROID_STUBIFY
+#include "android.h"
+#endif /* ANDROID_STUBIFY */
+#endif /* HAVE_ANDROID */
+
#include "sysselect.h" /* FIXME */
#include "systhread.h"
Lisp_Object event_object;
/* event_object must be the last Lisp field. */
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+ /* Pointer to an object to call Java functions through. */
+ JNIEnv *java_env;
+#endif /* HAVE_ANDROID && !ANDROID_STUBIFY */
+
/* An address near the bottom of the stack.
Tells GC how to save a copy of the stack. */
char const *m_stack_bottom;