aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPhilipp Stephani2019-04-18 17:42:45 +0200
committerPhilipp Stephani2019-04-24 11:33:52 +0200
commitbffceab6339fb4042588b893ef754c6264379e75 (patch)
tree03747b5cb5275a24418aa9fb421977eb6c0ee063
parent5ae407aad4f2564fae7ddce077eb01aa8efa37fb (diff)
downloademacs-bffceab6339fb4042588b893ef754c6264379e75.tar.gz
emacs-bffceab6339fb4042588b893ef754c6264379e75.zip
Add conversions to and from struct timespec to module interface.
Time values are a fundamental data type, and such conversions are hard to implement within modules because of the various forms of time values in Emacs Lisp. Adding dedicated conversion functions can significantly simplify module code dealing with times. This approach uses nanosecond precision. While Emacs in theory has support for higher-precision time values, in practice most languages and standards, such as POSIX, C, Java, and Go, have settled on nanosecond-precision integers to represent time. * src/emacs-module.h.in: Add header for struct timespec. * src/module-env-27.h: Add module functions for time conversion. * src/emacs-module.c (module_extract_time, module_make_time): New functions. (initialize_environment): Use them. * test/data/emacs-module/mod-test.c (Fmod_test_add_nanosecond): New test function. (emacs_module_init): Define it. * test/src/emacs-module-tests.el (mod-test-add-nanosecond/valid) (mod-test-add-nanosecond/nil, mod-test-add-nanosecond/invalid): New unit tests. * doc/lispref/internals.texi (Module Values): Document time conversion functions.
-rw-r--r--doc/lispref/internals.texi44
-rw-r--r--etc/NEWS3
-rw-r--r--src/emacs-module.c17
-rw-r--r--src/emacs-module.h.in1
-rw-r--r--src/module-env-27.h6
-rw-r--r--test/data/emacs-module/mod-test.c13
-rw-r--r--test/src/emacs-module-tests.el28
7 files changed, 112 insertions, 0 deletions
diff --git a/doc/lispref/internals.texi b/doc/lispref/internals.texi
index 25892d4b57c..0e7a1339e76 100644
--- a/doc/lispref/internals.texi
+++ b/doc/lispref/internals.texi
@@ -1387,6 +1387,38 @@ This function returns the value of a Lisp float specified by
1387@var{arg}, as a C @code{double} value. 1387@var{arg}, as a C @code{double} value.
1388@end deftypefn 1388@end deftypefn
1389 1389
1390@deftypefn Function struct timespec extract_time (emacs_env *@var{env}, emacs_value @var{time})
1391This function, which is available since Emacs 27, interprets
1392@var{time} as an Emacs Lisp time value and returns the corresponding
1393@code{struct timespec}. @xref{Time of Day}. @code{struct timespec}
1394represents a timestamp with nanosecond precision. It has the
1395following members:
1396
1397@table @code
1398@item time_t tv_sec
1399Whole number of seconds.
1400@item long tv_nsec
1401Fractional seconds as number of nanoseconds, always less than one
1402billion.
1403@end table
1404
1405@noindent
1406@xref{Elapsed Time,,,libc}.
1407
1408If @var{time} has higher precision than nanoseconds, then this
1409function truncates it to nanosecond precision. This function signals
1410an error if @var{time} (truncated to nanoseconds) cannot be
1411represented by @code{struct timespec}. For example, if @code{time_t}
1412is a 32-bit integral type, then a @var{time} value of ten billion
1413seconds would signal an error, but a @var{time} value of 600
1414picoseconds would get truncated to zero.
1415
1416If you need to deal with time values that are not representable by
1417@code{struct timespec}, or if you want higher precision, call the Lisp
1418function @code{encode-time} and work with its return value.
1419@xref{Time Conversion}.
1420@end deftypefn
1421
1390@deftypefn Function bool copy_string_contents (emacs_env *@var{env}, emacs_value @var{arg}, char *@var{buf}, ptrdiff_t *@var{len}) 1422@deftypefn Function bool copy_string_contents (emacs_env *@var{env}, emacs_value @var{arg}, char *@var{buf}, ptrdiff_t *@var{len})
1391This function stores the UTF-8 encoded text of a Lisp string specified 1423This function stores the UTF-8 encoded text of a Lisp string specified
1392by @var{arg} in the array of @code{char} pointed by @var{buf}, which 1424by @var{arg} in the array of @code{char} pointed by @var{buf}, which
@@ -1452,6 +1484,18 @@ This function takes a @code{double} argument @var{d} and returns the
1452corresponding Emacs floating-point value. 1484corresponding Emacs floating-point value.
1453@end deftypefn 1485@end deftypefn
1454 1486
1487@deftypefn Function emacs_value make_time (emacs_env *@var{env}, struct timespec @var{time})
1488This function, which is available since Emacs 27, takes a @code{struct
1489timespec} argument @var{time} and returns the corresponding Emacs
1490timestamp as a pair @code{(@var{ticks} . @var{hz})}. @xref{Time of
1491Day}. The return value represents exactly the same timestamp as
1492@var{time}: all input values are representable, and there is never a
1493loss of precision. @code{@var{time}.tv_sec} and
1494@code{@var{time}.tv_nsec} can be arbitrary values. In particular,
1495there's no requirement that @var{time} be normalized. This means that
1496@code{@var{time}.tv_nsec} can be negative or larger than 999,999,999.
1497@end deftypefn
1498
1455@deftypefn Function emacs_value make_string (emacs_env *@var{env}, const char *@var{str}, ptrdiff_t @var{strlen}) 1499@deftypefn Function emacs_value make_string (emacs_env *@var{env}, const char *@var{str}, ptrdiff_t @var{strlen})
1456This function creates an Emacs string from C text string pointed by 1500This function creates an Emacs string from C text string pointed by
1457@var{str} whose length in bytes, not including the terminating null 1501@var{str} whose length in bytes, not including the terminating null
diff --git a/etc/NEWS b/etc/NEWS
index b13ab47768b..fc9b828baa1 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -1910,6 +1910,9 @@ returns a regexp that never matches anything, which is an identity for
1910this operation. Previously, the empty string was returned in this 1910this operation. Previously, the empty string was returned in this
1911case. 1911case.
1912 1912
1913** New module environment functions 'make_time' and 'extract_time' to
1914convert between timespec structures and Emacs Lisp time values.
1915
1913 1916
1914* Changes in Emacs 27.1 on Non-Free Operating Systems 1917* Changes in Emacs 27.1 on Non-Free Operating Systems
1915 1918
diff --git a/src/emacs-module.c b/src/emacs-module.c
index b812fdc2df4..e46af30ce84 100644
--- a/src/emacs-module.c
+++ b/src/emacs-module.c
@@ -77,6 +77,7 @@ To add a new module function, proceed as follows:
77#include <stdint.h> 77#include <stdint.h>
78#include <stdio.h> 78#include <stdio.h>
79#include <stdlib.h> 79#include <stdlib.h>
80#include <time.h>
80 81
81#include "lisp.h" 82#include "lisp.h"
82#include "dynlib.h" 83#include "dynlib.h"
@@ -737,6 +738,20 @@ module_process_input (emacs_env *env)
737 return emacs_process_input_continue; 738 return emacs_process_input_continue;
738} 739}
739 740
741static struct timespec
742module_extract_time (emacs_env *env, emacs_value value)
743{
744 MODULE_FUNCTION_BEGIN ((struct timespec) {0});
745 return lisp_time_argument (value_to_lisp (value));
746}
747
748static emacs_value
749module_make_time (emacs_env *env, struct timespec time)
750{
751 MODULE_FUNCTION_BEGIN (NULL);
752 return lisp_to_value (env, make_lisp_time (time));
753}
754
740 755
741/* Subroutines. */ 756/* Subroutines. */
742 757
@@ -1140,6 +1155,8 @@ initialize_environment (emacs_env *env, struct emacs_env_private *priv)
1140 env->vec_size = module_vec_size; 1155 env->vec_size = module_vec_size;
1141 env->should_quit = module_should_quit; 1156 env->should_quit = module_should_quit;
1142 env->process_input = module_process_input; 1157 env->process_input = module_process_input;
1158 env->extract_time = module_extract_time;
1159 env->make_time = module_make_time;
1143 Vmodule_environments = Fcons (make_mint_ptr (env), Vmodule_environments); 1160 Vmodule_environments = Fcons (make_mint_ptr (env), Vmodule_environments);
1144 return env; 1161 return env;
1145} 1162}
diff --git a/src/emacs-module.h.in b/src/emacs-module.h.in
index 009d1583fef..bfbe226dd90 100644
--- a/src/emacs-module.h.in
+++ b/src/emacs-module.h.in
@@ -22,6 +22,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
22 22
23#include <stdint.h> 23#include <stdint.h>
24#include <stddef.h> 24#include <stddef.h>
25#include <time.h>
25 26
26#ifndef __cplusplus 27#ifndef __cplusplus
27#include <stdbool.h> 28#include <stdbool.h>
diff --git a/src/module-env-27.h b/src/module-env-27.h
index b491b60fbbc..e63843f8d63 100644
--- a/src/module-env-27.h
+++ b/src/module-env-27.h
@@ -2,3 +2,9 @@
2 function should quit. */ 2 function should quit. */
3 enum emacs_process_input_result (*process_input) (emacs_env *env) 3 enum emacs_process_input_result (*process_input) (emacs_env *env)
4 EMACS_ATTRIBUTE_NONNULL (1); 4 EMACS_ATTRIBUTE_NONNULL (1);
5
6 struct timespec (*extract_time) (emacs_env *env, emacs_value value)
7 EMACS_ATTRIBUTE_NONNULL (1);
8
9 emacs_value (*make_time) (emacs_env *env, struct timespec time)
10 EMACS_ATTRIBUTE_NONNULL (1);
diff --git a/test/data/emacs-module/mod-test.c b/test/data/emacs-module/mod-test.c
index a39e41afee6..dbdbfecfe6a 100644
--- a/test/data/emacs-module/mod-test.c
+++ b/test/data/emacs-module/mod-test.c
@@ -366,6 +366,18 @@ Fmod_test_sleep_until (emacs_env *env, ptrdiff_t nargs, emacs_value *args,
366 return env->intern (env, "finished"); 366 return env->intern (env, "finished");
367} 367}
368 368
369static emacs_value
370Fmod_test_add_nanosecond (emacs_env *env, ptrdiff_t nargs, emacs_value *args,
371 void *data)
372{
373 assert (nargs == 1);
374 struct timespec time = env->extract_time (env, args[0]);
375 assert (time.tv_nsec >= 0);
376 assert (time.tv_nsec < 2000000000); /* possible leap second */
377 time.tv_nsec++;
378 return env->make_time (env, time);
379}
380
369/* Lisp utilities for easier readability (simple wrappers). */ 381/* Lisp utilities for easier readability (simple wrappers). */
370 382
371/* Provide FEATURE to Emacs. */ 383/* Provide FEATURE to Emacs. */
@@ -434,6 +446,7 @@ emacs_module_init (struct emacs_runtime *ert)
434 DEFUN ("mod-test-invalid-finalizer", Fmod_test_invalid_finalizer, 0, 0, 446 DEFUN ("mod-test-invalid-finalizer", Fmod_test_invalid_finalizer, 0, 0,
435 NULL, NULL); 447 NULL, NULL);
436 DEFUN ("mod-test-sleep-until", Fmod_test_sleep_until, 2, 2, NULL, NULL); 448 DEFUN ("mod-test-sleep-until", Fmod_test_sleep_until, 2, 2, NULL, NULL);
449 DEFUN ("mod-test-add-nanosecond", Fmod_test_add_nanosecond, 1, 1, NULL, NULL);
437 450
438#undef DEFUN 451#undef DEFUN
439 452
diff --git a/test/src/emacs-module-tests.el b/test/src/emacs-module-tests.el
index 35aaaa64b65..eea4c611655 100644
--- a/test/src/emacs-module-tests.el
+++ b/test/src/emacs-module-tests.el
@@ -310,4 +310,32 @@ Interactively, you can try hitting \\[keyboard-quit] to quit."
310 'finished)) 310 'finished))
311 (quit))))) 311 (quit)))))
312 312
313(ert-deftest mod-test-add-nanosecond/valid ()
314 (dolist (input (list
315 ;; Some realistic examples.
316 (current-time) (time-to-seconds)
317 (encode-time 12 34 5 6 7 2019 t)
318 ;; Various legacy timestamp forms.
319 '(123 456) '(123 456 789) '(123 456 789 6000)
320 ;; Corner case: this will result in a nanosecond
321 ;; value of 1000000000 after addition. The module
322 ;; code should handle this correctly.
323 '(123 65535 999999 999000)
324 ;; Seconds since the epoch.
325 123 123.45
326 ;; New (TICKS . HZ) format.
327 '(123456789 . 1000000000)))
328 (ert-info ((format "input: %s" input))
329 (should (time-equal-p (mod-test-add-nanosecond input)
330 (time-add input '(0 0 0 1000)))))))
331
332(ert-deftest mod-test-add-nanosecond/nil ()
333 (should (<= (float-time (mod-test-add-nanosecond nil))
334 (+ (float-time) 1e-9))))
335
336(ert-deftest mod-test-add-nanosecond/invalid ()
337 (dolist (input '(1.0e+INF 1.0e-INF 0.0e+NaN (123) (123.45 6 7) "foo" [1 2]))
338 (ert-info ((format "input: %s" input))
339 (should-error (mod-test-add-nanosecond input)))))
340
313;;; emacs-module-tests.el ends here 341;;; emacs-module-tests.el ends here