diff options
author | Philipp Stephani <phst@google.com> | 2020-03-26 17:22:25 +0100 |
---|---|---|
committer | Philipp Stephani <phst@google.com> | 2020-03-26 21:47:25 +0100 |
commit | d28b00476890f791a89b65007e5f20682b3eaa0d (patch) | |
tree | 3bb04c984ed5b74e661291b71579fe8d04070f69 /test/data | |
parent | 934b3c9ecc2b91723b9e5826080424ec1a90f264 (diff) | |
download | emacs-d28b00476890f791a89b65007e5f20682b3eaa0d.tar.gz |
Add a module function to open a file descriptor connected to a pipe.
A common complaint about the module API is that modules can't
communicate asynchronously with Emacs. While it isn't possible to
call arbitrary Emacs functions asynchronously, writing to a pipe
should always be fine and is a pretty low-hanging fruit.
This patch implements a function that adapts an existing pipe
process. That way, users can use familiar tools like process filters
or 'accept-process-output'.
* src/module-env-28.h: Add 'open_channel' module function.
* src/emacs-module.c (module_open_channel): Provide definition for
'open_channel'.
(initialize_environment): Use it.
* src/process.c (open_channel_for_module): New helper function.
(syms_of_process): Define necessary symbol.
* test/src/emacs-module-tests.el (module/async-pipe): New unit test.
* test/data/emacs-module/mod-test.c (signal_system_error): New helper
function.
(signal_errno): Use it.
(write_to_pipe): New function running in the background.
(Fmod_test_async_pipe): New test module function.
(emacs_module_init): Export it.
* doc/lispref/internals.texi (Module Misc): Document new module
function.
* doc/lispref/processes.texi (Asynchronous Processes): New anchor
for pipe processes.
* etc/NEWS: Document 'open_channel' function.
Diffstat (limited to 'test/data')
-rw-r--r-- | test/data/emacs-module/mod-test.c | 57 |
1 files changed, 55 insertions, 2 deletions
diff --git a/test/data/emacs-module/mod-test.c b/test/data/emacs-module/mod-test.c index ec6948921f2..61733f1ef49 100644 --- a/test/data/emacs-module/mod-test.c +++ b/test/data/emacs-module/mod-test.c @@ -30,6 +30,9 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ #include <string.h> #include <time.h> +#include <pthread.h> +#include <unistd.h> + #ifdef HAVE_GMP #include <gmp.h> #else @@ -320,9 +323,9 @@ Fmod_test_invalid_finalizer (emacs_env *env, ptrdiff_t nargs, emacs_value *args, } static void -signal_errno (emacs_env *env, const char *function) +signal_system_error (emacs_env *env, int error, const char *function) { - const char *message = strerror (errno); + const char *message = strerror (error); emacs_value message_value = env->make_string (env, message, strlen (message)); emacs_value symbol = env->intern (env, "file-error"); emacs_value elements[2] @@ -331,6 +334,12 @@ signal_errno (emacs_env *env, const char *function) env->non_local_exit_signal (env, symbol, data); } +static void +signal_errno (emacs_env *env, const char *function) +{ + signal_system_error (env, errno, function); +} + /* A long-running operation that occasionally calls `should_quit' or `process_input'. */ @@ -533,6 +542,49 @@ Fmod_test_function_finalizer_calls (emacs_env *env, ptrdiff_t nargs, return env->funcall (env, Flist, 2, list_args); } +static void * +write_to_pipe (void *arg) +{ + /* We sleep a bit to test that writing to a pipe is indeed possible + if no environment is active. */ + const struct timespec sleep = {0, 500000000}; + if (nanosleep (&sleep, NULL) != 0) + perror ("nanosleep"); + FILE *stream = arg; + if (fputs ("data from thread", stream) < 0) + perror ("fputs"); + if (fclose (stream) != 0) + perror ("close"); + return NULL; +} + +static emacs_value +Fmod_test_async_pipe (emacs_env *env, ptrdiff_t nargs, emacs_value *args, + void *data) +{ + assert (nargs == 1); + int fd = env->open_channel (env, args[0]); + if (env->non_local_exit_check (env) != emacs_funcall_exit_return) + return NULL; + FILE *stream = fdopen (fd, "w"); + if (stream == NULL) + { + signal_errno (env, "fdopen"); + return NULL; + } + pthread_t thread; + int error + = pthread_create (&thread, NULL, write_to_pipe, stream); + if (error != 0) + { + signal_system_error (env, error, "pthread_create"); + if (fclose (stream) != 0) + perror ("fclose"); + return NULL; + } + return env->intern (env, "nil"); +} + /* Lisp utilities for easier readability (simple wrappers). */ /* Provide FEATURE to Emacs. */ @@ -614,6 +666,7 @@ emacs_module_init (struct emacs_runtime *ert) Fmod_test_make_function_with_finalizer, 0, 0, NULL, NULL); DEFUN ("mod-test-function-finalizer-calls", Fmod_test_function_finalizer_calls, 0, 0, NULL, NULL); + DEFUN ("mod-test-async-pipe", Fmod_test_async_pipe, 1, 1, NULL, NULL); #undef DEFUN |