diff options
| author | Philipp Stephani | 2020-03-26 17:22:25 +0100 |
|---|---|---|
| committer | Philipp Stephani | 2020-03-26 21:47:25 +0100 |
| commit | d28b00476890f791a89b65007e5f20682b3eaa0d (patch) | |
| tree | 3bb04c984ed5b74e661291b71579fe8d04070f69 /test/data | |
| parent | 934b3c9ecc2b91723b9e5826080424ec1a90f264 (diff) | |
| download | emacs-d28b00476890f791a89b65007e5f20682b3eaa0d.tar.gz emacs-d28b00476890f791a89b65007e5f20682b3eaa0d.zip | |
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/>. */ | |||
| 30 | #include <string.h> | 30 | #include <string.h> |
| 31 | #include <time.h> | 31 | #include <time.h> |
| 32 | 32 | ||
| 33 | #include <pthread.h> | ||
| 34 | #include <unistd.h> | ||
| 35 | |||
| 33 | #ifdef HAVE_GMP | 36 | #ifdef HAVE_GMP |
| 34 | #include <gmp.h> | 37 | #include <gmp.h> |
| 35 | #else | 38 | #else |
| @@ -320,9 +323,9 @@ Fmod_test_invalid_finalizer (emacs_env *env, ptrdiff_t nargs, emacs_value *args, | |||
| 320 | } | 323 | } |
| 321 | 324 | ||
| 322 | static void | 325 | static void |
| 323 | signal_errno (emacs_env *env, const char *function) | 326 | signal_system_error (emacs_env *env, int error, const char *function) |
| 324 | { | 327 | { |
| 325 | const char *message = strerror (errno); | 328 | const char *message = strerror (error); |
| 326 | emacs_value message_value = env->make_string (env, message, strlen (message)); | 329 | emacs_value message_value = env->make_string (env, message, strlen (message)); |
| 327 | emacs_value symbol = env->intern (env, "file-error"); | 330 | emacs_value symbol = env->intern (env, "file-error"); |
| 328 | emacs_value elements[2] | 331 | emacs_value elements[2] |
| @@ -331,6 +334,12 @@ signal_errno (emacs_env *env, const char *function) | |||
| 331 | env->non_local_exit_signal (env, symbol, data); | 334 | env->non_local_exit_signal (env, symbol, data); |
| 332 | } | 335 | } |
| 333 | 336 | ||
| 337 | static void | ||
| 338 | signal_errno (emacs_env *env, const char *function) | ||
| 339 | { | ||
| 340 | signal_system_error (env, errno, function); | ||
| 341 | } | ||
| 342 | |||
| 334 | /* A long-running operation that occasionally calls `should_quit' or | 343 | /* A long-running operation that occasionally calls `should_quit' or |
| 335 | `process_input'. */ | 344 | `process_input'. */ |
| 336 | 345 | ||
| @@ -533,6 +542,49 @@ Fmod_test_function_finalizer_calls (emacs_env *env, ptrdiff_t nargs, | |||
| 533 | return env->funcall (env, Flist, 2, list_args); | 542 | return env->funcall (env, Flist, 2, list_args); |
| 534 | } | 543 | } |
| 535 | 544 | ||
| 545 | static void * | ||
| 546 | write_to_pipe (void *arg) | ||
| 547 | { | ||
| 548 | /* We sleep a bit to test that writing to a pipe is indeed possible | ||
| 549 | if no environment is active. */ | ||
| 550 | const struct timespec sleep = {0, 500000000}; | ||
| 551 | if (nanosleep (&sleep, NULL) != 0) | ||
| 552 | perror ("nanosleep"); | ||
| 553 | FILE *stream = arg; | ||
| 554 | if (fputs ("data from thread", stream) < 0) | ||
| 555 | perror ("fputs"); | ||
| 556 | if (fclose (stream) != 0) | ||
| 557 | perror ("close"); | ||
| 558 | return NULL; | ||
| 559 | } | ||
| 560 | |||
| 561 | static emacs_value | ||
| 562 | Fmod_test_async_pipe (emacs_env *env, ptrdiff_t nargs, emacs_value *args, | ||
| 563 | void *data) | ||
| 564 | { | ||
| 565 | assert (nargs == 1); | ||
| 566 | int fd = env->open_channel (env, args[0]); | ||
| 567 | if (env->non_local_exit_check (env) != emacs_funcall_exit_return) | ||
| 568 | return NULL; | ||
| 569 | FILE *stream = fdopen (fd, "w"); | ||
| 570 | if (stream == NULL) | ||
| 571 | { | ||
| 572 | signal_errno (env, "fdopen"); | ||
| 573 | return NULL; | ||
| 574 | } | ||
| 575 | pthread_t thread; | ||
| 576 | int error | ||
| 577 | = pthread_create (&thread, NULL, write_to_pipe, stream); | ||
| 578 | if (error != 0) | ||
| 579 | { | ||
| 580 | signal_system_error (env, error, "pthread_create"); | ||
| 581 | if (fclose (stream) != 0) | ||
| 582 | perror ("fclose"); | ||
| 583 | return NULL; | ||
| 584 | } | ||
| 585 | return env->intern (env, "nil"); | ||
| 586 | } | ||
| 587 | |||
| 536 | /* Lisp utilities for easier readability (simple wrappers). */ | 588 | /* Lisp utilities for easier readability (simple wrappers). */ |
| 537 | 589 | ||
| 538 | /* Provide FEATURE to Emacs. */ | 590 | /* Provide FEATURE to Emacs. */ |
| @@ -614,6 +666,7 @@ emacs_module_init (struct emacs_runtime *ert) | |||
| 614 | Fmod_test_make_function_with_finalizer, 0, 0, NULL, NULL); | 666 | Fmod_test_make_function_with_finalizer, 0, 0, NULL, NULL); |
| 615 | DEFUN ("mod-test-function-finalizer-calls", | 667 | DEFUN ("mod-test-function-finalizer-calls", |
| 616 | Fmod_test_function_finalizer_calls, 0, 0, NULL, NULL); | 668 | Fmod_test_function_finalizer_calls, 0, 0, NULL, NULL); |
| 669 | DEFUN ("mod-test-async-pipe", Fmod_test_async_pipe, 1, 1, NULL, NULL); | ||
| 617 | 670 | ||
| 618 | #undef DEFUN | 671 | #undef DEFUN |
| 619 | 672 | ||