Fully support Lisp threads on Android
* src/android.c (struct android_thread_event_queue): New structure. Move global pselect arguments, mutexes, and semaphores, and pipes here. (struct android_event_queue) <select_mutex, select_thread>: Remove to the above-named struct. (android_init_thread_events, android_finalize_thread_events) (android_get_poll_thread): New functions. (android_run_select_thread): Accept a set of mutexes and thread-local data as the first argument, and operate with them rather than globals. (android_handle_sigusr1): Rename to `android_handle_poll_signal'. Set thread-specific cancellation indicator. (android_init_events): Properly abort after a fatal condition. Enable interruptible polling on Android 5.1 and later, not 4.2. (android_select): Never check for queries here, but in thread_select, if threads are enabled. Adapt to per-thread polling threads and only enable interruptible polling on Android 5.1 and later. (android_before_select): New function. * src/android.h (android_before_select): New declaration. * src/thread.c (thread_select): Call `android_before_select' before the global lock is released.
This commit is contained in:
parent
8b9194ae03
commit
8df582a468
3 changed files with 344 additions and 186 deletions
526
src/android.c
526
src/android.c
|
@ -266,57 +266,191 @@ struct android_event_container
|
|||
union android_event event;
|
||||
};
|
||||
|
||||
struct android_event_queue
|
||||
{
|
||||
/* Mutex protecting the event queue. */
|
||||
pthread_mutex_t mutex;
|
||||
/* Thread-specific component of the Android event queue. */
|
||||
|
||||
struct android_thread_event_queue
|
||||
{
|
||||
/* Mutex protecting the select data. */
|
||||
pthread_mutex_t select_mutex;
|
||||
|
||||
/* The thread used to run select. */
|
||||
pthread_t select_thread;
|
||||
|
||||
/* Arguments to pselect used by the select thread. */
|
||||
fd_set *select_readfds;
|
||||
fd_set *select_writefds;
|
||||
fd_set *select_exceptfds;
|
||||
struct timespec *select_timeout;
|
||||
int select_nfds;
|
||||
|
||||
/* Semaphores posted around invocations of pselect. */
|
||||
sem_t start_sem;
|
||||
sem_t select_sem;
|
||||
|
||||
/* Value of pselect. */
|
||||
int select_rc;
|
||||
|
||||
#if __ANDROID_API__ < 21
|
||||
/* Select self-pipe. */
|
||||
int select_pipe[2];
|
||||
#else /* __ANDROID_API__ >= 21 */
|
||||
/* Whether a signal has been received to cancel pselect in this
|
||||
thread. */
|
||||
volatile sig_atomic_t cancel_signal_received;
|
||||
#endif /* __ANDROID_API__ >= 21 */
|
||||
|
||||
/* Whether this thread must exit. */
|
||||
int canceled;
|
||||
};
|
||||
|
||||
#if __ANDROID_API__ >= 21
|
||||
#define SELECT_SIGNAL SIGUSR1
|
||||
#endif /* __ANDROID_API__ >= 21 */
|
||||
|
||||
struct android_event_queue
|
||||
{
|
||||
/* Mutex protecting the event queue. */
|
||||
pthread_mutex_t mutex;
|
||||
|
||||
/* Condition variables for the reading side. */
|
||||
pthread_cond_t read_var;
|
||||
|
||||
/* The number of events in the queue. If this is greater than 1024,
|
||||
writing will block. */
|
||||
int num_events;
|
||||
|
||||
/* Circular queue of events. */
|
||||
struct android_event_container events;
|
||||
|
||||
#ifndef THREADS_ENABLED
|
||||
/* If threads are disabled, the thread-specific component of the main
|
||||
and only thread. */
|
||||
struct android_thread_event_queue thread;
|
||||
#endif /* !THREADS_ENABLED */
|
||||
|
||||
/* The number of events in the queue. */
|
||||
int num_events;
|
||||
};
|
||||
|
||||
/* Arguments to pselect used by the select thread. */
|
||||
static int android_pselect_nfds;
|
||||
static fd_set *android_pselect_readfds;
|
||||
static fd_set *android_pselect_writefds;
|
||||
static fd_set *android_pselect_exceptfds;
|
||||
static struct timespec *android_pselect_timeout;
|
||||
|
||||
/* Value of pselect. */
|
||||
static int android_pselect_rc;
|
||||
|
||||
/* The global event queue. */
|
||||
static struct android_event_queue event_queue;
|
||||
|
||||
/* Semaphores used to signal select completion and start. */
|
||||
static sem_t android_pselect_sem, android_pselect_start_sem;
|
||||
/* Main select loop of select threads. */
|
||||
static void *android_run_select_thread (void *);
|
||||
|
||||
#if __ANDROID_API__ < 16
|
||||
/* Initialize a thread-local component of the Android event queue
|
||||
THREAD. Create and initialize a thread whose purpose is to execute
|
||||
`select' in an interruptible manner, and initialize variables or file
|
||||
descriptors with which to communicate with it. */
|
||||
|
||||
/* Select self-pipe. */
|
||||
static int select_pipe[2];
|
||||
static void
|
||||
android_init_thread_events (struct android_thread_event_queue *thread)
|
||||
{
|
||||
thread->canceled = false;
|
||||
thread->select_readfds = NULL;
|
||||
thread->select_writefds = NULL;
|
||||
thread->select_exceptfds = NULL;
|
||||
thread->select_timeout = NULL;
|
||||
thread->select_nfds = 0;
|
||||
|
||||
#else
|
||||
if (pthread_mutex_init (&thread->select_mutex, NULL))
|
||||
{
|
||||
__android_log_print (ANDROID_LOG_FATAL, __func__,
|
||||
"pthread_mutex_init: %s",
|
||||
strerror (errno));
|
||||
emacs_abort ();
|
||||
}
|
||||
|
||||
/* Whether or not pselect has been interrupted. */
|
||||
static volatile sig_atomic_t android_pselect_interrupted;
|
||||
sem_init (&thread->select_sem, 0, 0);
|
||||
sem_init (&thread->start_sem, 0, 0);
|
||||
|
||||
#endif
|
||||
#if __ANDROID_API__ < 21
|
||||
/* Set up the file descriptor used to wake up pselect. */
|
||||
if (pipe2 (thread->select_pipe, O_CLOEXEC) < 0)
|
||||
{
|
||||
__android_log_print (ANDROID_LOG_FATAL, __func__,
|
||||
"pipe2: %s", strerror (errno));
|
||||
emacs_abort ();
|
||||
}
|
||||
|
||||
/* Set the task name of the current task to NAME, a string at most 16
|
||||
/* Make sure the read end will fit in fd_set. */
|
||||
if (thread->select_pipe[0] >= FD_SETSIZE)
|
||||
{
|
||||
__android_log_print (ANDROID_LOG_FATAL, __func__,
|
||||
"read end of select pipe"
|
||||
" exceeds FD_SETSIZE!");
|
||||
emacs_abort ();
|
||||
}
|
||||
#endif /* __ANDROID_API__ < 21 */
|
||||
|
||||
/* Start the select thread. */
|
||||
if (pthread_create (&thread->select_thread, NULL,
|
||||
android_run_select_thread, thread))
|
||||
{
|
||||
__android_log_print (ANDROID_LOG_FATAL, __func__,
|
||||
"pthread_create: %s",
|
||||
strerror (errno));
|
||||
emacs_abort ();
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef THREADS_ENABLED
|
||||
|
||||
/* Destroy a thread-local component of the Android event queue provided
|
||||
as DATA, and release DATA's storage itself. Must be invoked at a
|
||||
time when the select thread is idle, i.e., awaiting
|
||||
DATA->start_sem. */
|
||||
|
||||
static void
|
||||
android_finalize_thread_events (void *data)
|
||||
{
|
||||
int rc;
|
||||
struct android_thread_event_queue *thread;
|
||||
|
||||
/* Cancel the thread and pause till it exits. */
|
||||
thread = data;
|
||||
thread->canceled = 1;
|
||||
sem_post (&thread->start_sem);
|
||||
rc = pthread_join (thread->select_thread, NULL);
|
||||
if (rc)
|
||||
emacs_abort ();
|
||||
|
||||
/* Release the select thread, semaphores, etc. */
|
||||
pthread_mutex_destroy (&thread->select_mutex);
|
||||
sem_close (&thread->select_sem);
|
||||
sem_close (&thread->start_sem);
|
||||
#if __ANDROID_API__ < 21
|
||||
close (thread->select_pipe[0]);
|
||||
close (thread->select_pipe[1]);
|
||||
#endif /* __ANDROID_API__ < 21 */
|
||||
xfree (thread);
|
||||
}
|
||||
|
||||
/* TLS keys associating polling threads with Emacs threads. */
|
||||
static pthread_key_t poll_thread, poll_thread_internal;
|
||||
|
||||
#endif /* THREADS_ENABLED */
|
||||
|
||||
/* Return the thread-specific component of the event queue appertaining
|
||||
to this thread, or create it as well as a polling thread if
|
||||
absent. */
|
||||
|
||||
static struct android_thread_event_queue *
|
||||
android_get_poll_thread (void)
|
||||
{
|
||||
#ifndef THREADS_ENABLED
|
||||
return &event_queue.thread;
|
||||
#else /* THREADS_ENABLED */
|
||||
struct android_thread_event_queue *queue;
|
||||
|
||||
queue = pthread_getspecific (poll_thread);
|
||||
if (!queue)
|
||||
{
|
||||
queue = xmalloc (sizeof *queue);
|
||||
android_init_thread_events (queue);
|
||||
pthread_setspecific (poll_thread, queue);
|
||||
}
|
||||
return queue;
|
||||
#endif /* !THREADS_ENABLED */
|
||||
}
|
||||
|
||||
/* Set the task name of the current task to NAME, a string at most 21
|
||||
characters in length.
|
||||
|
||||
This name is displayed as that of the task (LWP)'s pthread in
|
||||
|
@ -357,67 +491,73 @@ android_set_task_name (const char *name)
|
|||
}
|
||||
|
||||
static void *
|
||||
android_run_select_thread (void *data)
|
||||
android_run_select_thread (void *thread_data)
|
||||
{
|
||||
/* Apparently this is required too. */
|
||||
JNI_STACK_ALIGNMENT_PROLOGUE;
|
||||
|
||||
int rc;
|
||||
#if __ANDROID_API__ < 16
|
||||
struct android_thread_event_queue *data;
|
||||
#if __ANDROID_API__ < 21
|
||||
int nfds;
|
||||
fd_set readfds;
|
||||
char byte;
|
||||
#else
|
||||
#else /* __ANDROID_API__ >= 21 */
|
||||
sigset_t signals, waitset;
|
||||
int sig;
|
||||
#endif
|
||||
#endif /* __ANDROID_API__ >= 21 */
|
||||
|
||||
/* Set the name of this thread's LWP for debugging purposes. */
|
||||
android_set_task_name ("`android_select'");
|
||||
android_set_task_name ("Emacs polling thread");
|
||||
data = thread_data;
|
||||
|
||||
#if __ANDROID_API__ < 16
|
||||
#if __ANDROID_API__ < 21
|
||||
/* A completely different implementation is used when building for
|
||||
Android versions earlier than 16, because pselect with a signal
|
||||
mask does not work there. Instead of blocking SIGUSR1 and
|
||||
unblocking it inside pselect, a file descriptor is used instead.
|
||||
Something is written to the file descriptor every time select is
|
||||
supposed to return. */
|
||||
Android versions earlier than 21, because pselect with a signal
|
||||
mask does not work properly: the signal mask is truncated on APIs
|
||||
<= 16, and elsewhere, the signal mask is applied in userspace
|
||||
before issuing a select system call, between which SELECT_SIGNAL
|
||||
may arrive. Instead of blocking SELECT_SIGNAL and unblocking it
|
||||
inside pselect, a file descriptor is selected. Data is written to
|
||||
the file descriptor whenever select is supposed to return. */
|
||||
|
||||
while (true)
|
||||
{
|
||||
/* Wait for the thread to be released. */
|
||||
while (sem_wait (&android_pselect_start_sem) < 0)
|
||||
while (sem_wait (&data->start_sem) < 0)
|
||||
;;
|
||||
if (data->canceled)
|
||||
return NULL;
|
||||
|
||||
/* Get the select lock and call pselect. API 8 does not have
|
||||
working pselect in any sense. Instead, pselect wakes up on
|
||||
select_pipe[0]. */
|
||||
|
||||
pthread_mutex_lock (&event_queue.select_mutex);
|
||||
nfds = android_pselect_nfds;
|
||||
pthread_mutex_lock (&data->select_mutex);
|
||||
nfds = data->select_nfds;
|
||||
|
||||
if (android_pselect_readfds)
|
||||
readfds = *android_pselect_readfds;
|
||||
if (data->select_readfds)
|
||||
readfds = *data->select_readfds;
|
||||
else
|
||||
FD_ZERO (&readfds);
|
||||
|
||||
if (nfds < select_pipe[0] + 1)
|
||||
nfds = select_pipe[0] + 1;
|
||||
FD_SET (select_pipe[0], &readfds);
|
||||
if (nfds < data->select_pipe[0] + 1)
|
||||
nfds = data->select_pipe[0] + 1;
|
||||
FD_SET (data->select_pipe[0], &readfds);
|
||||
|
||||
rc = pselect (nfds, &readfds,
|
||||
android_pselect_writefds,
|
||||
android_pselect_exceptfds,
|
||||
android_pselect_timeout,
|
||||
data->select_writefds,
|
||||
data->select_exceptfds,
|
||||
data->select_timeout,
|
||||
NULL);
|
||||
|
||||
/* Subtract 1 from rc if readfds contains the select pipe, and
|
||||
also remove it from that set. */
|
||||
|
||||
if (rc != -1 && FD_ISSET (select_pipe[0], &readfds))
|
||||
if (rc != -1 && FD_ISSET (data->select_pipe[0], &readfds))
|
||||
{
|
||||
rc -= 1;
|
||||
FD_CLR (select_pipe[0], &readfds);
|
||||
FD_CLR (data->select_pipe[0], &readfds);
|
||||
|
||||
/* If no file descriptors aside from the select pipe are
|
||||
ready, then pretend that an error has occurred. */
|
||||
|
@ -427,11 +567,11 @@ android_run_select_thread (void *data)
|
|||
|
||||
/* Save the read file descriptor set back again. */
|
||||
|
||||
if (android_pselect_readfds)
|
||||
*android_pselect_readfds = readfds;
|
||||
if (data->select_readfds)
|
||||
*data->select_readfds = readfds;
|
||||
|
||||
android_pselect_rc = rc;
|
||||
pthread_mutex_unlock (&event_queue.select_mutex);
|
||||
data->select_rc = rc;
|
||||
pthread_mutex_unlock (&data->select_mutex);
|
||||
|
||||
/* Signal the main thread that there is now data to read. Hold
|
||||
the event queue lock during this process to make sure this
|
||||
|
@ -443,44 +583,46 @@ android_run_select_thread (void *data)
|
|||
pthread_mutex_unlock (&event_queue.mutex);
|
||||
|
||||
/* Read a single byte from the select pipe. */
|
||||
read (select_pipe[0], &byte, 1);
|
||||
read (data->select_pipe[0], &byte, 1);
|
||||
|
||||
/* Signal the Emacs thread that pselect is done. If read_var
|
||||
was signaled by android_write_event, event_queue.mutex could
|
||||
still be locked, so this must come before. */
|
||||
sem_post (&android_pselect_sem);
|
||||
sem_post (&data->select_sem);
|
||||
}
|
||||
#else
|
||||
#else /* __ANDROID_API__ >= 21 */
|
||||
sigfillset (&signals);
|
||||
if (pthread_sigmask (SIG_BLOCK, &signals, NULL))
|
||||
__android_log_print (ANDROID_LOG_FATAL, __func__,
|
||||
"pthread_sigmask: %s",
|
||||
strerror (errno));
|
||||
|
||||
sigfillset (&signals);
|
||||
sigdelset (&signals, SIGUSR1);
|
||||
sigdelset (&signals, SELECT_SIGNAL);
|
||||
sigemptyset (&waitset);
|
||||
sigaddset (&waitset, SIGUSR1);
|
||||
sigaddset (&waitset, SELECT_SIGNAL);
|
||||
#ifdef THREADS_ENABLED
|
||||
pthread_setspecific (poll_thread_internal, thread_data);
|
||||
#endif /* THREADS_ENABLED */
|
||||
|
||||
while (true)
|
||||
{
|
||||
/* Wait for the thread to be released. */
|
||||
while (sem_wait (&android_pselect_start_sem) < 0)
|
||||
while (sem_wait (&data->start_sem) < 0)
|
||||
;;
|
||||
|
||||
/* Clear the ``pselect interrupted'' flag. This is safe because
|
||||
right now, SIGUSR1 is blocked. */
|
||||
android_pselect_interrupted = 0;
|
||||
if (data->canceled)
|
||||
return NULL;
|
||||
|
||||
/* Get the select lock and call pselect. */
|
||||
pthread_mutex_lock (&event_queue.select_mutex);
|
||||
rc = pselect (android_pselect_nfds,
|
||||
android_pselect_readfds,
|
||||
android_pselect_writefds,
|
||||
android_pselect_exceptfds,
|
||||
android_pselect_timeout,
|
||||
data->cancel_signal_received = 0;
|
||||
pthread_mutex_lock (&data->select_mutex);
|
||||
rc = pselect (data->select_nfds,
|
||||
data->select_readfds,
|
||||
data->select_writefds,
|
||||
data->select_exceptfds,
|
||||
data->select_timeout,
|
||||
&signals);
|
||||
android_pselect_rc = rc;
|
||||
pthread_mutex_unlock (&event_queue.select_mutex);
|
||||
data->select_rc = rc;
|
||||
pthread_mutex_unlock (&data->select_mutex);
|
||||
|
||||
/* Signal the main thread that there is now data to read. Hold
|
||||
the event queue lock during this process to make sure this
|
||||
|
@ -491,39 +633,51 @@ android_run_select_thread (void *data)
|
|||
pthread_cond_broadcast (&event_queue.read_var);
|
||||
pthread_mutex_unlock (&event_queue.mutex);
|
||||
|
||||
/* Check `android_pselect_interrupted' instead of rc and errno.
|
||||
/* Test a separate flag `data->cancel_signal_received' rather than
|
||||
rc and errno.
|
||||
|
||||
This is because `pselect' does not return an rc of -1 upon
|
||||
being interrupted in some versions of Android, but does set
|
||||
signal masks correctly. */
|
||||
|
||||
if (!android_pselect_interrupted)
|
||||
/* Now, wait for SIGUSR1, unless pselect was interrupted and
|
||||
the signal was already delivered. The Emacs thread will
|
||||
always send this signal after read_var is triggered or the
|
||||
UI thread has sent an event. */
|
||||
if (!data->cancel_signal_received)
|
||||
/* Now, wait for SELECT_SIGNAL, unless pselect was interrupted
|
||||
and the signal has already been delivered. The Emacs thread
|
||||
will always send this signal after read_var is triggered or
|
||||
the UI thread has sent an event. */
|
||||
sigwait (&waitset, &sig);
|
||||
|
||||
/* Signal the Emacs thread that pselect is done. If read_var
|
||||
was signaled by android_write_event, event_queue.mutex could
|
||||
still be locked, so this must come before. */
|
||||
sem_post (&android_pselect_sem);
|
||||
sem_post (&data->select_sem);
|
||||
}
|
||||
#endif
|
||||
#endif /* __ANDROID_API__ >= 21 */
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#if __ANDROID_API__ >= 16
|
||||
#if __ANDROID_API__ >= 21
|
||||
|
||||
static void
|
||||
android_handle_sigusr1 (int sig, siginfo_t *siginfo, void *arg)
|
||||
android_handle_poll_signal (int sig, siginfo_t *siginfo, void *arg)
|
||||
{
|
||||
/* Notice that pselect has been interrupted. */
|
||||
android_pselect_interrupted = 1;
|
||||
struct android_thread_event_queue *queue;
|
||||
|
||||
/* Although pthread_getspecific is not AS-safe, its implementation has
|
||||
been verified to be safe to invoke from a single handler called
|
||||
within pselect in a controlled manner, and this is the only means
|
||||
of retrieving thread-specific data from a signal handler, as the
|
||||
POSIX real-time signal system calls are unavailable to Android
|
||||
applications. */
|
||||
#ifdef THREADS_ENABLED
|
||||
queue = pthread_getspecific (poll_thread_internal);
|
||||
#else /* !THREADS_ENABLED */
|
||||
queue = &event_queue.thread;
|
||||
#endif /* !THREADS_ENABLED */
|
||||
queue->cancel_signal_received = 1;
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif /* __ANDROID_API__ >= 21 */
|
||||
|
||||
/* Semaphore used to indicate completion of a query.
|
||||
This should ideally be defined further down. */
|
||||
|
@ -543,66 +697,57 @@ static pthread_t main_thread_id;
|
|||
static void
|
||||
android_init_events (void)
|
||||
{
|
||||
#if __ANDROID_API__ >= 21
|
||||
struct sigaction sa;
|
||||
#endif /* __ANDROID_API__ >= 21 */
|
||||
|
||||
if (pthread_mutex_init (&event_queue.mutex, NULL))
|
||||
__android_log_print (ANDROID_LOG_FATAL, __func__,
|
||||
"pthread_mutex_init: %s",
|
||||
strerror (errno));
|
||||
|
||||
if (pthread_mutex_init (&event_queue.select_mutex, NULL))
|
||||
__android_log_print (ANDROID_LOG_FATAL, __func__,
|
||||
"pthread_mutex_init: %s",
|
||||
strerror (errno));
|
||||
{
|
||||
__android_log_print (ANDROID_LOG_FATAL, __func__,
|
||||
"pthread_mutex_init: %s",
|
||||
strerror (errno));
|
||||
emacs_abort ();
|
||||
}
|
||||
|
||||
if (pthread_cond_init (&event_queue.read_var, NULL))
|
||||
__android_log_print (ANDROID_LOG_FATAL, __func__,
|
||||
"pthread_cond_init: %s",
|
||||
strerror (errno));
|
||||
|
||||
sem_init (&android_pselect_sem, 0, 0);
|
||||
sem_init (&android_pselect_start_sem, 0, 0);
|
||||
sem_init (&android_query_sem, 0, 0);
|
||||
{
|
||||
__android_log_print (ANDROID_LOG_FATAL, __func__,
|
||||
"pthread_cond_init: %s",
|
||||
strerror (errno));
|
||||
emacs_abort ();
|
||||
}
|
||||
|
||||
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
|
||||
SIGUSR1 is correct. */
|
||||
#if __ANDROID_API__ >= 21
|
||||
/* Before any event threads are initialized, guarantee that the
|
||||
disposition of SELECT_SIGNAL is correct. */
|
||||
sigfillset (&sa.sa_mask);
|
||||
sa.sa_sigaction = android_handle_sigusr1;
|
||||
sa.sa_sigaction = android_handle_poll_signal;
|
||||
sa.sa_flags = SA_SIGINFO;
|
||||
|
||||
#else
|
||||
|
||||
/* Set up the file descriptor used to wake up pselect. */
|
||||
if (pipe2 (select_pipe, O_CLOEXEC) < 0)
|
||||
__android_log_print (ANDROID_LOG_FATAL, __func__,
|
||||
"pipe2: %s", strerror (errno));
|
||||
|
||||
/* Make sure the read end will fit in fd_set. */
|
||||
if (select_pipe[0] >= FD_SETSIZE)
|
||||
__android_log_print (ANDROID_LOG_FATAL, __func__,
|
||||
"read end of select pipe"
|
||||
" lies outside FD_SETSIZE!");
|
||||
|
||||
#endif
|
||||
|
||||
if (sigaction (SIGUSR1, &sa, NULL))
|
||||
__android_log_print (ANDROID_LOG_FATAL, __func__,
|
||||
"sigaction: %s",
|
||||
strerror (errno));
|
||||
|
||||
/* Start the select thread. */
|
||||
if (pthread_create (&event_queue.select_thread, NULL,
|
||||
android_run_select_thread, NULL))
|
||||
__android_log_print (ANDROID_LOG_FATAL, __func__,
|
||||
"pthread_create: %s",
|
||||
strerror (errno));
|
||||
if (sigaction (SELECT_SIGNAL, &sa, NULL))
|
||||
{
|
||||
__android_log_print (ANDROID_LOG_FATAL, __func__,
|
||||
"sigaction: %s",
|
||||
strerror (errno));
|
||||
emacs_abort ();
|
||||
}
|
||||
#endif /* __ANDROID_API__ >= 21 */
|
||||
#ifndef THREADS_ENABLED
|
||||
android_init_thread_events (&event_queue.thread);
|
||||
#else /* THREADS_ENABLED */
|
||||
if (pthread_key_create (&poll_thread, android_finalize_thread_events)
|
||||
|| pthread_key_create (&poll_thread_internal, NULL))
|
||||
{
|
||||
__android_log_print (ANDROID_LOG_FATAL, __func__,
|
||||
"pthread_key_create: %s",
|
||||
strerror (errno));
|
||||
emacs_abort ();
|
||||
}
|
||||
#endif /* THREADS_ENABLED */
|
||||
}
|
||||
|
||||
int
|
||||
|
@ -761,25 +906,17 @@ int
|
|||
android_select (int nfds, fd_set *readfds, fd_set *writefds,
|
||||
fd_set *exceptfds, struct timespec *timeout)
|
||||
{
|
||||
int nfds_return;
|
||||
#if __ANDROID_API__ < 16
|
||||
int nfds_return, nevents;
|
||||
#if __ANDROID_API__ < 21
|
||||
static char byte;
|
||||
#endif
|
||||
struct android_thread_event_queue *data;
|
||||
|
||||
#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''. */
|
||||
|
||||
__atomic_clear (&android_urgent_query, __ATOMIC_RELEASE);
|
||||
|
||||
/* Check for and run anything the UI thread wants to run on the main
|
||||
thread. */
|
||||
android_check_query ();
|
||||
/* When threads are enabled, the following is executed before the
|
||||
global lock is released. */
|
||||
#ifndef THREADS_ENABLED
|
||||
android_before_select ();
|
||||
#endif /* !THREADS_ENABLED */
|
||||
|
||||
pthread_mutex_lock (&event_queue.mutex);
|
||||
|
||||
|
@ -804,60 +941,60 @@ android_select (int nfds, fd_set *readfds, fd_set *writefds,
|
|||
|
||||
nfds_return = 0;
|
||||
|
||||
pthread_mutex_lock (&event_queue.select_mutex);
|
||||
android_pselect_nfds = nfds;
|
||||
android_pselect_readfds = readfds;
|
||||
android_pselect_writefds = writefds;
|
||||
android_pselect_exceptfds = exceptfds;
|
||||
android_pselect_timeout = timeout;
|
||||
pthread_mutex_unlock (&event_queue.select_mutex);
|
||||
data = android_get_poll_thread ();
|
||||
|
||||
pthread_mutex_lock (&data->select_mutex);
|
||||
data->select_nfds = nfds;
|
||||
data->select_readfds = readfds;
|
||||
data->select_writefds = writefds;
|
||||
data->select_exceptfds = exceptfds;
|
||||
data->select_timeout = timeout;
|
||||
pthread_mutex_unlock (&data->select_mutex);
|
||||
|
||||
/* Release the select thread. */
|
||||
sem_post (&android_pselect_start_sem);
|
||||
sem_post (&data->start_sem);
|
||||
|
||||
/* Start waiting for the event queue condition to be set. */
|
||||
pthread_cond_wait (&event_queue.read_var, &event_queue.mutex);
|
||||
|
||||
#if __ANDROID_API__ >= 16
|
||||
#if __ANDROID_API__ >= 21
|
||||
/* Interrupt the select thread now, in case it's still in
|
||||
pselect. */
|
||||
pthread_kill (event_queue.select_thread, SIGUSR1);
|
||||
#else
|
||||
pthread_kill (data->select_thread, SELECT_SIGNAL);
|
||||
#else /* __ANDROID_API__ < 21 */
|
||||
/* Interrupt the select thread by writing to the select pipe. */
|
||||
if (write (select_pipe[1], &byte, 1) != 1)
|
||||
if (write (data->select_pipe[1], &byte, 1) != 1)
|
||||
__android_log_print (ANDROID_LOG_FATAL, __func__,
|
||||
"write: %s", strerror (errno));
|
||||
#endif
|
||||
#endif /* __ANDROID_API__ < 21 */
|
||||
|
||||
/* Unlock the event queue mutex. */
|
||||
/* Are there any events in the event queue? */
|
||||
nevents = event_queue.num_events;
|
||||
pthread_mutex_unlock (&event_queue.mutex);
|
||||
|
||||
/* Wait for pselect to return in any case. This must be done with
|
||||
the event queue mutex unlocked. Otherwise, the pselect thread
|
||||
can hang if it tries to lock the event queue mutex to signal
|
||||
read_var after the UI thread has already done so. */
|
||||
while (sem_wait (&android_pselect_sem) < 0)
|
||||
/* Wait for pselect to return in any case. This must be done with the
|
||||
event queue mutex unlocked. Otherwise, the pselect thread can hang
|
||||
if it tries to lock the event queue mutex to signal read_var after
|
||||
the UI thread has already done so. */
|
||||
while (sem_wait (&data->select_sem) < 0)
|
||||
;;
|
||||
|
||||
/* If there are now events in the queue, return 1. */
|
||||
|
||||
pthread_mutex_lock (&event_queue.mutex);
|
||||
if (event_queue.num_events)
|
||||
if (nevents)
|
||||
nfds_return = 1;
|
||||
pthread_mutex_unlock (&event_queue.mutex);
|
||||
|
||||
/* Add the return value of pselect if it has also found ready file
|
||||
descriptors. */
|
||||
/* Add the return value of pselect if it has also discovered ready
|
||||
file descriptors. */
|
||||
|
||||
if (android_pselect_rc >= 0)
|
||||
nfds_return += android_pselect_rc;
|
||||
if (data->select_rc >= 0)
|
||||
nfds_return += data->select_rc;
|
||||
else if (!nfds_return)
|
||||
/* If pselect was interrupted and nfds_return is 0 (meaning that
|
||||
no events have been read), indicate that an error has taken
|
||||
/* If pselect was interrupted and nfds_return is 0 (meaning that no
|
||||
events have been read), indicate that an error has taken
|
||||
place. */
|
||||
nfds_return = android_pselect_rc;
|
||||
nfds_return = data->select_rc;
|
||||
|
||||
if ((android_pselect_rc < 0) && nfds_return >= 0)
|
||||
if ((data->select_rc < 0) && nfds_return >= 0)
|
||||
{
|
||||
/* Clear the file descriptor sets if events will be delivered
|
||||
but no file descriptors have become ready to prevent the
|
||||
|
@ -6721,6 +6858,23 @@ static void *android_query_context;
|
|||
itself; however, the input signal handler executes a memory fence
|
||||
to ensure that all query related writes become visible. */
|
||||
|
||||
/* Clear the ``urgent query'' flag and run any function that the UI
|
||||
thread has asked to run. Must be invoked before `android_select'
|
||||
from the thread holding the global lock. */
|
||||
|
||||
void
|
||||
android_before_select (void)
|
||||
{
|
||||
/* Since Emacs is reading keyboard input again, signify that queries
|
||||
from input methods are no longer ``urgent''. */
|
||||
|
||||
__atomic_clear (&android_urgent_query, __ATOMIC_RELEASE);
|
||||
|
||||
/* Check for and run anything the UI thread wants to run on the main
|
||||
thread. */
|
||||
android_check_query ();
|
||||
}
|
||||
|
||||
/* Run any function that the UI thread has asked to run, and then
|
||||
signal its completion. */
|
||||
|
||||
|
@ -6781,7 +6935,7 @@ android_check_query_urgent (void)
|
|||
if (!proc)
|
||||
return;
|
||||
|
||||
proc (closure);
|
||||
(*proc) (closure);
|
||||
|
||||
/* Finish the query. Don't clear `android_urgent_query'; instead,
|
||||
do that the next time Emacs enters the keyboard loop. */
|
||||
|
@ -6931,8 +7085,8 @@ android_run_in_emacs_thread (void (*proc) (void *), void *closure)
|
|||
/* Send a dummy event. `android_check_query' will be called inside
|
||||
wait_reading_process_output after the event arrives.
|
||||
|
||||
Otherwise, android_select will call android_check_thread the next
|
||||
time it is entered. */
|
||||
Otherwise, android_select will call `android_check_query' when next
|
||||
it is entered. */
|
||||
android_write_event (&event);
|
||||
|
||||
/* Start waiting for the function to be executed. First, wait two
|
||||
|
|
|
@ -244,6 +244,7 @@ extern void android_display_toast (const char *);
|
|||
|
||||
/* Event loop functions. */
|
||||
|
||||
extern void android_before_select (void);
|
||||
extern void android_check_query (void);
|
||||
extern void android_check_query_urgent (void);
|
||||
extern int android_run_in_emacs_thread (void (*) (void *), void *);
|
||||
|
|
|
@ -653,6 +653,9 @@ thread_select (select_func *func, int max_fds, fd_set *rfds,
|
|||
sa.efds = efds;
|
||||
sa.timeout = timeout;
|
||||
sa.sigmask = sigmask;
|
||||
#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
|
||||
android_before_select ();
|
||||
#endif /* HAVE_ANDROID && !defined ANDROID_STUBIFY */
|
||||
flush_stack_call_func (really_call_select, &sa);
|
||||
return sa.result;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue