aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPhilipp Stephani2018-02-11 21:38:22 +0100
committerPhilipp Stephani2020-01-03 19:24:10 +0100
commit48ffef5ef4b34799941a033591ea827d40025939 (patch)
tree67b00c1bc546f3c9ef601c10db634da3094f7f57
parent2b6d702e5d2d572640c6bcd43f54138bacbe7ac8 (diff)
downloademacs-48ffef5ef4b34799941a033591ea827d40025939.tar.gz
emacs-48ffef5ef4b34799941a033591ea827d40025939.zip
Implement finalizers for module functions (Bug#30373)
* src/module-env-28.h: Add new module environment functions to module environment for Emacs 28. * src/emacs-module.h.in: Document that 'emacs_finalizer' also works for function finalizers. * src/emacs-module.c (CHECK_MODULE_FUNCTION): New function. (struct Lisp_Module_Function): Add finalizer data member. (module_make_function): Initialize finalizer. (module_get_function_finalizer) (module_set_function_finalizer): New module environment functions. (module_finalize_function): New function. (initialize_environment): Initialize new environment functions. * src/alloc.c (cleanup_vector): Call potential module function finalizer during garbage collection. * test/data/emacs-module/mod-test.c (signal_error): New helper function. (memory_full): Use it. (finalizer): New example function finalizer. (Fmod_test_make_function_with_finalizer) (Fmod_test_function_finalizer_calls): New test module functions. (emacs_module_init): Define them. * test/src/emacs-module-tests.el (module/function-finalizer): New unit test. * doc/lispref/internals.texi (Module Functions): Document new functionality. (Module Misc): Move description of 'emacs_finalizer' type to 'Module Functions' node, and add a reference to it. * etc/NEWS: Mention new functionality.
-rw-r--r--doc/lispref/internals.texi55
-rw-r--r--etc/NEWS5
-rw-r--r--src/alloc.c6
-rw-r--r--src/emacs-module.c36
-rw-r--r--src/emacs-module.h.in4
-rw-r--r--src/lisp.h1
-rw-r--r--src/module-env-28.h8
-rw-r--r--test/data/emacs-module/mod-test.c49
-rw-r--r--test/src/emacs-module-tests.el8
9 files changed, 163 insertions, 9 deletions
diff --git a/doc/lispref/internals.texi b/doc/lispref/internals.texi
index d95a3e445cc..c0b3fe5a1b0 100644
--- a/doc/lispref/internals.texi
+++ b/doc/lispref/internals.texi
@@ -1447,6 +1447,54 @@ The Lisp package which goes with your module could then load the
1447module using the @code{load} primitive (@pxref{Dynamic Modules}) when 1447module using the @code{load} primitive (@pxref{Dynamic Modules}) when
1448the package is loaded into Emacs. 1448the package is loaded into Emacs.
1449 1449
1450@anchor{Module Function Finalizers}
1451If you want to run some code when a module function object (i.e., an
1452object returned by @code{make_function}) is garbage-collected, you can
1453install a @dfn{function finalizer}. Function finalizers are available
1454since Emacs 28. For example, if you have passed some heap-allocated
1455structure to the @var{data} argument of @code{make_function}, you can
1456use the finalizer to deallocate the structure. @xref{Basic
1457Allocation,,,libc}, and @pxref{Freeing after Malloc,,,libc}. The
1458finalizer function has the following signature:
1459
1460@example
1461void finalizer (void *@var{data})
1462@end example
1463
1464Here, @var{data} receives the value passed to @var{data} when calling
1465@code{make_function}. Note that the finalizer can't interact with
1466Emacs in any way.
1467
1468Directly after calling @code{make_function}, the newly-created
1469function doesn't have a finalizer. Use @code{set_function_finalizer}
1470to add one, if desired.
1471
1472@deftypefun void emacs_finalizer (void *@var{ptr})
1473The header @file{emacs-module.h} provides the type
1474@code{emacs_finalizer} as a type alias for an Emacs finalizer
1475function.
1476@end deftypefun
1477
1478@deftypefun emacs_finalizer get_function_finalizer (emacs_env *@var{env}, emacs_value @var{arg})
1479This function, which is available since Emacs 28, returns the function
1480finalizer associated with the module function represented by
1481@var{arg}. @var{arg} must refer to a module function, that is, an
1482object returned by @code{make_function}. If no finalizer is
1483associated with the function, @code{NULL} is returned.
1484@end deftypefun
1485
1486@deftypefun void set_function_finalizer (emacs_env *@var{env}, emacs_value @var{arg}, emacs_finalizer @var{fin})
1487This function, which is available since Emacs 28, sets the function
1488finalizer associated with the module function represented by @var{arg}
1489to @var{fin}. @var{arg} must refer to a module function, that is, an
1490object returned by @code{make_function}. @var{fin} can either be
1491@code{NULL} to clear @var{arg}'s function finalizer, or a pointer to a
1492function to be called when the object represented by @var{arg} is
1493garbage-collected. At most one function finalizer can be set per
1494function; if @var{arg} already has a finalizer, it is replaced by
1495@var{fin}.
1496@end deftypefun
1497
1450@node Module Values 1498@node Module Values
1451@subsection Conversion Between Lisp and Module Values 1499@subsection Conversion Between Lisp and Module Values
1452@cindex module values, conversion 1500@cindex module values, conversion
@@ -1865,11 +1913,8 @@ represented by @var{arg} to be @var{fin}. If @var{fin} is a
1865finalizer. 1913finalizer.
1866@end deftypefn 1914@end deftypefn
1867 1915
1868@deftypefun void emacs_finalizer (void *@var{ptr}) 1916Note that the @code{emacs_finalizer} type works for both user pointer
1869The header @file{emacs-module.h} provides the type 1917an module function finalizers. @xref{Module Function Finalizers}.
1870@code{emacs_finalizer} as a type alias for an Emacs finalizer
1871function.
1872@end deftypefun
1873 1918
1874@node Module Misc 1919@node Module Misc
1875@subsection Miscellaneous Convenience Functions for Modules 1920@subsection Miscellaneous Convenience Functions for Modules
diff --git a/etc/NEWS b/etc/NEWS
index df12c7e8430..d6cabf8e9e4 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -49,6 +49,11 @@ applies, and please also update docstrings as needed.
49'emacs_function' and 'emacs_finalizer' for module functions and 49'emacs_function' and 'emacs_finalizer' for module functions and
50finalizers, respectively. 50finalizers, respectively.
51 51
52** Module functions can now install an optional finalizer that is
53called when the function object is garbage-collected. Use
54'set_function_finalizer' to set the finalizer and
55'get_function_finalizer' to retrieve it.
56
52 57
53* Changes in Emacs 28.1 on Non-Free Operating Systems 58* Changes in Emacs 28.1 on Non-Free Operating Systems
54 59
diff --git a/src/alloc.c b/src/alloc.c
index dbe37f44d7c..f59f8cbde9a 100644
--- a/src/alloc.c
+++ b/src/alloc.c
@@ -3027,6 +3027,12 @@ cleanup_vector (struct Lisp_Vector *vector)
3027 if (uptr->finalizer) 3027 if (uptr->finalizer)
3028 uptr->finalizer (uptr->p); 3028 uptr->finalizer (uptr->p);
3029 } 3029 }
3030 else if (PSEUDOVECTOR_TYPEP (&vector->header, PVEC_MODULE_FUNCTION))
3031 {
3032 ATTRIBUTE_MAY_ALIAS struct Lisp_Module_Function *function
3033 = (struct Lisp_Module_Function *) vector;
3034 module_finalize_function (function);
3035 }
3030} 3036}
3031 3037
3032/* Reclaim space used by unmarked vectors. */ 3038/* Reclaim space used by unmarked vectors. */
diff --git a/src/emacs-module.c b/src/emacs-module.c
index bbb0e3dadd9..3855a33f254 100644
--- a/src/emacs-module.c
+++ b/src/emacs-module.c
@@ -327,6 +327,12 @@ static bool module_assertions = false;
327 MODULE_HANDLE_NONLOCAL_EXIT (error_retval) 327 MODULE_HANDLE_NONLOCAL_EXIT (error_retval)
328 328
329static void 329static void
330CHECK_MODULE_FUNCTION (Lisp_Object obj)
331{
332 CHECK_TYPE (MODULE_FUNCTIONP (obj), Qmodule_function_p, obj);
333}
334
335static void
330CHECK_USER_PTR (Lisp_Object obj) 336CHECK_USER_PTR (Lisp_Object obj)
331{ 337{
332 CHECK_TYPE (USER_PTRP (obj), Quser_ptrp, obj); 338 CHECK_TYPE (USER_PTRP (obj), Quser_ptrp, obj);
@@ -478,6 +484,7 @@ struct Lisp_Module_Function
478 ptrdiff_t min_arity, max_arity; 484 ptrdiff_t min_arity, max_arity;
479 emacs_function subr; 485 emacs_function subr;
480 void *data; 486 void *data;
487 emacs_finalizer finalizer;
481} GCALIGNED_STRUCT; 488} GCALIGNED_STRUCT;
482 489
483static struct Lisp_Module_Function * 490static struct Lisp_Module_Function *
@@ -511,6 +518,7 @@ module_make_function (emacs_env *env, ptrdiff_t min_arity, ptrdiff_t max_arity,
511 function->max_arity = max_arity; 518 function->max_arity = max_arity;
512 function->subr = func; 519 function->subr = func;
513 function->data = data; 520 function->data = data;
521 function->finalizer = NULL;
514 522
515 if (docstring) 523 if (docstring)
516 function->documentation = build_string_from_utf8 (docstring); 524 function->documentation = build_string_from_utf8 (docstring);
@@ -522,6 +530,32 @@ module_make_function (emacs_env *env, ptrdiff_t min_arity, ptrdiff_t max_arity,
522 return lisp_to_value (env, result); 530 return lisp_to_value (env, result);
523} 531}
524 532
533static emacs_finalizer
534module_get_function_finalizer (emacs_env *env, emacs_value arg)
535{
536 MODULE_FUNCTION_BEGIN (NULL);
537 Lisp_Object lisp = value_to_lisp (arg);
538 CHECK_MODULE_FUNCTION (lisp);
539 return XMODULE_FUNCTION (lisp)->finalizer;
540}
541
542static void
543module_set_function_finalizer (emacs_env *env, emacs_value arg,
544 emacs_finalizer fin)
545{
546 MODULE_FUNCTION_BEGIN ();
547 Lisp_Object lisp = value_to_lisp (arg);
548 CHECK_MODULE_FUNCTION (lisp);
549 XMODULE_FUNCTION (lisp)->finalizer = fin;
550}
551
552void
553module_finalize_function (const struct Lisp_Module_Function *func)
554{
555 if (func->finalizer != NULL)
556 func->finalizer (func->data);
557}
558
525static emacs_value 559static emacs_value
526module_funcall (emacs_env *env, emacs_value func, ptrdiff_t nargs, 560module_funcall (emacs_env *env, emacs_value func, ptrdiff_t nargs,
527 emacs_value *args) 561 emacs_value *args)
@@ -1329,6 +1363,8 @@ initialize_environment (emacs_env *env, struct emacs_env_private *priv)
1329 env->make_time = module_make_time; 1363 env->make_time = module_make_time;
1330 env->extract_big_integer = module_extract_big_integer; 1364 env->extract_big_integer = module_extract_big_integer;
1331 env->make_big_integer = module_make_big_integer; 1365 env->make_big_integer = module_make_big_integer;
1366 env->get_function_finalizer = module_get_function_finalizer;
1367 env->set_function_finalizer = module_set_function_finalizer;
1332 Vmodule_environments = Fcons (make_mint_ptr (env), Vmodule_environments); 1368 Vmodule_environments = Fcons (make_mint_ptr (env), Vmodule_environments);
1333 return env; 1369 return env;
1334} 1370}
diff --git a/src/emacs-module.h.in b/src/emacs-module.h.in
index 7065f13f2b1..b5ddd7d5fd8 100644
--- a/src/emacs-module.h.in
+++ b/src/emacs-module.h.in
@@ -90,8 +90,8 @@ typedef emacs_value (*emacs_function) (emacs_env *env, ptrdiff_t nargs,
90 void *data) 90 void *data)
91 EMACS_NOEXCEPT EMACS_ATTRIBUTE_NONNULL (1); 91 EMACS_NOEXCEPT EMACS_ATTRIBUTE_NONNULL (1);
92 92
93/* Function prototype for module user-pointer finalizers. These must 93/* Function prototype for module user-pointer and function finalizers.
94 not throw C++ exceptions. */ 94 These must not throw C++ exceptions. */
95typedef void (*emacs_finalizer) (void *data) EMACS_NOEXCEPT; 95typedef void (*emacs_finalizer) (void *data) EMACS_NOEXCEPT;
96 96
97/* Possible Emacs function call outcomes. */ 97/* Possible Emacs function call outcomes. */
diff --git a/src/lisp.h b/src/lisp.h
index 356692d53a1..36bb79d67e1 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -4244,6 +4244,7 @@ extern Lisp_Object module_function_documentation
4244 (struct Lisp_Module_Function const *); 4244 (struct Lisp_Module_Function const *);
4245extern module_funcptr module_function_address 4245extern module_funcptr module_function_address
4246 (struct Lisp_Module_Function const *); 4246 (struct Lisp_Module_Function const *);
4247extern void module_finalize_function (const struct Lisp_Module_Function *);
4247extern void mark_modules (void); 4248extern void mark_modules (void);
4248extern void init_module_assertions (bool); 4249extern void init_module_assertions (bool);
4249extern void syms_of_module (void); 4250extern void syms_of_module (void);
diff --git a/src/module-env-28.h b/src/module-env-28.h
index dec8704edde..a2479a8f744 100644
--- a/src/module-env-28.h
+++ b/src/module-env-28.h
@@ -1,3 +1,11 @@
1 /* Add module environment functions newly added in Emacs 28 here. 1 /* Add module environment functions newly added in Emacs 28 here.
2 Before Emacs 28 is released, remove this comment and start 2 Before Emacs 28 is released, remove this comment and start
3 module-env-29.h on the master branch. */ 3 module-env-29.h on the master branch. */
4
5 void (*(*EMACS_ATTRIBUTE_NONNULL (1)
6 get_function_finalizer) (emacs_env *env,
7 emacs_value arg)) (void *) EMACS_NOEXCEPT;
8
9 void (*set_function_finalizer) (emacs_env *env, emacs_value arg,
10 void (*fin) (void *) EMACS_NOEXCEPT)
11 EMACS_ATTRIBUTE_NONNULL (1);
diff --git a/test/data/emacs-module/mod-test.c b/test/data/emacs-module/mod-test.c
index 8dc9ff144af..1a0a879a1bc 100644
--- a/test/data/emacs-module/mod-test.c
+++ b/test/data/emacs-module/mod-test.c
@@ -373,15 +373,20 @@ Fmod_test_add_nanosecond (emacs_env *env, ptrdiff_t nargs, emacs_value *args,
373} 373}
374 374
375static void 375static void
376memory_full (emacs_env *env) 376signal_error (emacs_env *env, const char *message)
377{ 377{
378 const char *message = "Memory exhausted";
379 emacs_value data = env->make_string (env, message, strlen (message)); 378 emacs_value data = env->make_string (env, message, strlen (message));
380 env->non_local_exit_signal (env, env->intern (env, "error"), 379 env->non_local_exit_signal (env, env->intern (env, "error"),
381 env->funcall (env, env->intern (env, "list"), 1, 380 env->funcall (env, env->intern (env, "list"), 1,
382 &data)); 381 &data));
383} 382}
384 383
384static void
385memory_full (emacs_env *env)
386{
387 signal_error (env, "Memory exhausted");
388}
389
385enum 390enum
386{ 391{
387 max_count = ((SIZE_MAX < PTRDIFF_MAX ? SIZE_MAX : PTRDIFF_MAX) 392 max_count = ((SIZE_MAX < PTRDIFF_MAX ? SIZE_MAX : PTRDIFF_MAX)
@@ -490,6 +495,42 @@ Fmod_test_double (emacs_env *env, ptrdiff_t nargs, emacs_value *args,
490 return result; 495 return result;
491} 496}
492 497
498static int function_data;
499static int finalizer_calls_with_correct_data;
500static int finalizer_calls_with_incorrect_data;
501
502static void
503finalizer (void *data)
504{
505 if (data == &function_data)
506 ++finalizer_calls_with_correct_data;
507 else
508 ++finalizer_calls_with_incorrect_data;
509}
510
511static emacs_value
512Fmod_test_make_function_with_finalizer (emacs_env *env, ptrdiff_t nargs,
513 emacs_value *args, void *data)
514{
515 emacs_value fun
516 = env->make_function (env, 2, 2, Fmod_test_sum, NULL, &function_data);
517 env->set_function_finalizer (env, fun, finalizer);
518 if (env->get_function_finalizer (env, fun) != finalizer)
519 signal_error (env, "Invalid finalizer");
520 return fun;
521}
522
523static emacs_value
524Fmod_test_function_finalizer_calls (emacs_env *env, ptrdiff_t nargs,
525 emacs_value *args, void *data)
526{
527 emacs_value Flist = env->intern (env, "list");
528 emacs_value list_args[]
529 = {env->make_integer (env, finalizer_calls_with_correct_data),
530 env->make_integer (env, finalizer_calls_with_incorrect_data)};
531 return env->funcall (env, Flist, 2, list_args);
532}
533
493/* Lisp utilities for easier readability (simple wrappers). */ 534/* Lisp utilities for easier readability (simple wrappers). */
494 535
495/* Provide FEATURE to Emacs. */ 536/* Provide FEATURE to Emacs. */
@@ -566,6 +607,10 @@ emacs_module_init (struct emacs_runtime *ert)
566 DEFUN ("mod-test-add-nanosecond", Fmod_test_add_nanosecond, 1, 1, NULL, NULL); 607 DEFUN ("mod-test-add-nanosecond", Fmod_test_add_nanosecond, 1, 1, NULL, NULL);
567 DEFUN ("mod-test-nanoseconds", Fmod_test_nanoseconds, 1, 1, NULL, NULL); 608 DEFUN ("mod-test-nanoseconds", Fmod_test_nanoseconds, 1, 1, NULL, NULL);
568 DEFUN ("mod-test-double", Fmod_test_double, 1, 1, NULL, NULL); 609 DEFUN ("mod-test-double", Fmod_test_double, 1, 1, NULL, NULL);
610 DEFUN ("mod-test-make-function-with-finalizer",
611 Fmod_test_make_function_with_finalizer, 0, 0, NULL, NULL);
612 DEFUN ("mod-test-function-finalizer-calls",
613 Fmod_test_function_finalizer_calls, 0, 0, NULL, NULL);
569 614
570#undef DEFUN 615#undef DEFUN
571 616
diff --git a/test/src/emacs-module-tests.el b/test/src/emacs-module-tests.el
index a2cb3e9b498..4f5871be5eb 100644
--- a/test/src/emacs-module-tests.el
+++ b/test/src/emacs-module-tests.el
@@ -402,4 +402,12 @@ See Bug#36226."
402 (load so nil nil :nosuffix :must-suffix) 402 (load so nil nil :nosuffix :must-suffix)
403 (delete-file so)))) 403 (delete-file so))))
404 404
405(ert-deftest module/function-finalizer ()
406 (mod-test-make-function-with-finalizer)
407 (let* ((previous-calls (mod-test-function-finalizer-calls))
408 (expected-calls (copy-sequence previous-calls)))
409 (cl-incf (car expected-calls))
410 (garbage-collect)
411 (should (equal (mod-test-function-finalizer-calls) expected-calls))))
412
405;;; emacs-module-tests.el ends here 413;;; emacs-module-tests.el ends here