Skip to content

Commit f173b8b

Browse files
committed
NIF: Add erlang/os system_time/0 and monotonic_time/0
Add zero-arity NIF implementations for erlang:system_time/0, erlang:monotonic_time/0, and os:system_time/0,1, returning time in native units. erlang:system_time/0 and erlang:monotonic_time/0 delegate to their /1 counterparts with native os:system_time/0,1 reuse the erlang:system_time NIF directly to avoid code duplication Added Erlang stubs/exports in erlang.erl and os.erl Added Elixir wrappers: System.monotonic_time/0, System.system_time/0, System.os_time/0,1 Added tests for all new functions Signed-off-by: Peter M <petermm@gmail.com>
1 parent 668b1b7 commit f173b8b

File tree

9 files changed

+183
-19
lines changed

9 files changed

+183
-19
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
8181
- Added missing `inet` functions: `ntoa/1`, `parse_address/1`, `parse_ipv4_address/1`,
8282
`parse_ipv4strict_address/1`
8383
- Added `nanosecond` and `native` time unit support to `erlang:system_time/1`, `erlang:monotonic_time/1`, and `calendar:system_time_to_universal_time/2`
84+
- Added `erlang:system_time/0`, `erlang:monotonic_time/0`, and `os:system_time/0,1` NIFs
8485

8586
### Changed
8687

libs/estdlib/src/erlang.erl

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
is_record/2,
4141
map_size/1,
4242
map_get/2,
43+
monotonic_time/0,
4344
monotonic_time/1,
4445
min/2,
4546
max/2,
@@ -104,6 +105,7 @@
104105
exit/1,
105106
exit/2,
106107
open_port/2,
108+
system_time/0,
107109
system_time/1,
108110
group_leader/0,
109111
group_leader/2,
@@ -568,6 +570,10 @@ memory(_Type) ->
568570
%% systems the value may vary among UNIX systems (e.g., Linux, macOS, FreeBSD).
569571
%% @end
570572
%%-----------------------------------------------------------------------------
573+
-spec monotonic_time() -> integer().
574+
monotonic_time() ->
575+
erlang:nif_error(undefined).
576+
571577
-spec monotonic_time(Unit :: time_unit()) -> integer().
572578
monotonic_time(_Unit) ->
573579
erlang:nif_error(undefined).
@@ -1250,6 +1256,15 @@ exit(_Process, _Reason) ->
12501256
open_port(_PortName, _Options) ->
12511257
erlang:nif_error(undefined).
12521258

1259+
%%-----------------------------------------------------------------------------
1260+
%% @returns An integer representing system time.
1261+
%% @doc Returns the current Erlang system time in native time unit.
1262+
%% @end
1263+
%%-----------------------------------------------------------------------------
1264+
-spec system_time() -> integer().
1265+
system_time() ->
1266+
erlang:nif_error(undefined).
1267+
12531268
%%-----------------------------------------------------------------------------
12541269
%% @param Unit Unit to return system time in
12551270
%% @returns An integer representing system time

libs/estdlib/src/os.erl

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
%
2020
-module(os).
2121

22-
-export([getenv/1]).
22+
-export([getenv/1, system_time/0, system_time/1]).
2323

2424
%%-----------------------------------------------------------------------------
2525
%% @param Name name of the environment variable
@@ -30,3 +30,21 @@
3030
-spec getenv(Name :: nonempty_string()) -> nonempty_string() | false.
3131
getenv(_VarName) ->
3232
erlang:nif_error(undefined).
33+
34+
%%-----------------------------------------------------------------------------
35+
%% @returns An integer representing system time.
36+
%% @doc Returns the current OS system time in native time unit.
37+
%% @end
38+
%%-----------------------------------------------------------------------------
39+
-spec system_time() -> integer().
40+
system_time() ->
41+
erlang:nif_error(undefined).
42+
43+
%%-----------------------------------------------------------------------------
44+
%% @returns An integer representing system time.
45+
%% @doc Returns the current OS system time in the time unit.
46+
%% @end
47+
%%-----------------------------------------------------------------------------
48+
-spec system_time(TimeUnit :: erlang:time_unit()) -> integer().
49+
system_time(TimeUnit) ->
50+
erlang:nif_error(undefined).

libs/exavmlib/lib/System.ex

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,21 @@ defmodule System do
2525
:second
2626
| :millisecond
2727
| :microsecond
28+
| :nanosecond
29+
30+
@doc """
31+
Returns the current monotonic time in the `:native` time unit.
32+
33+
This time is monotonically increasing and starts in an unspecified
34+
point in time. This is not strictly monotonically increasing. Multiple
35+
sequential calls of the function may return the same value.
36+
37+
Inlined by the compiler.
38+
"""
39+
@spec monotonic_time() :: integer
40+
def monotonic_time do
41+
:erlang.monotonic_time()
42+
end
2843

2944
@doc """
3045
Returns the current monotonic time in the given time unit.
@@ -37,6 +52,20 @@ defmodule System do
3752
:erlang.monotonic_time(unit)
3853
end
3954

55+
@doc """
56+
Returns the current system time in the `:native` time unit.
57+
58+
It is the VM view of the `os_time/0`. They may not match in
59+
case of time warps although the VM works towards aligning
60+
them. This time is not monotonic.
61+
62+
Inlined by the compiler.
63+
"""
64+
@spec system_time() :: integer
65+
def system_time do
66+
:erlang.system_time()
67+
end
68+
4069
@doc """
4170
Returns the current system time in the given time unit.
4271
@@ -48,4 +77,30 @@ defmodule System do
4877
def system_time(unit) do
4978
:erlang.system_time(unit)
5079
end
80+
81+
@doc """
82+
Returns the current operating system (OS) time.
83+
84+
The result is returned in the `:native` time unit.
85+
86+
This time may be adjusted forwards or backwards in time
87+
with no limitation and is not monotonic.
88+
89+
Inlined by the compiler.
90+
"""
91+
@spec os_time() :: integer
92+
def os_time do
93+
:os.system_time()
94+
end
95+
96+
@doc """
97+
Returns the current operating system (OS) time in the given time `unit`.
98+
99+
This time may be adjusted forwards or backwards in time
100+
with no limitation and is not monotonic.
101+
"""
102+
@spec os_time(time_unit | :native) :: integer
103+
def os_time(unit) do
104+
:os.system_time(unit)
105+
end
51106
end

src/libAtomVM/nifs.c

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ static term nif_erlang_float_to_list(Context *ctx, int argc, term argv[]);
187187
static term nif_erlang_list_to_binary_1(Context *ctx, int argc, term argv[]);
188188
static term nif_erlang_list_to_integer(Context *ctx, int argc, term argv[]);
189189
static term nif_erlang_list_to_float_1(Context *ctx, int argc, term argv[]);
190+
static term nif_erlang_monotonic_time_0(Context *ctx, int argc, term argv[]);
190191
static term nif_erlang_monotonic_time_1(Context *ctx, int argc, term argv[]);
191192
static term nif_erlang_iolist_size_1(Context *ctx, int argc, term argv[]);
192193
static term nif_erlang_iolist_to_binary_1(Context *ctx, int argc, term argv[]);
@@ -198,7 +199,9 @@ static term nif_erlang_setelement_3(Context *ctx, int argc, term argv[]);
198199
// static term nif_erlang_spawn_opt(Context *ctx, int argc, term argv[]);
199200
static term nif_erlang_spawn_fun_opt(Context *ctx, int argc, term argv[]);
200201
static term nif_erlang_whereis_1(Context *ctx, int argc, term argv[]);
202+
static term nif_erlang_system_time_0(Context *ctx, int argc, term argv[]);
201203
static term nif_erlang_system_time_1(Context *ctx, int argc, term argv[]);
204+
202205
static term nif_erlang_tuple_to_list_1(Context *ctx, int argc, term argv[]);
203206
static term nif_erlang_list_to_tuple_1(Context *ctx, int argc, term argv[]);
204207
static term nif_erlang_universaltime_0(Context *ctx, int argc, term argv[]);
@@ -533,11 +536,21 @@ static const struct Nif concat_nif = {
533536
.nif_ptr = nif_erlang_concat_2
534537
};
535538

539+
static const struct Nif monotonic_time_0_nif = {
540+
.base.type = NIFFunctionType,
541+
.nif_ptr = nif_erlang_monotonic_time_0
542+
};
543+
536544
static const struct Nif monotonic_time_nif = {
537545
.base.type = NIFFunctionType,
538546
.nif_ptr = nif_erlang_monotonic_time_1
539547
};
540548

549+
static const struct Nif system_time_0_nif = {
550+
.base.type = NIFFunctionType,
551+
.nif_ptr = nif_erlang_system_time_0
552+
};
553+
541554
static const struct Nif system_time_nif = {
542555
.base.type = NIFFunctionType,
543556
.nif_ptr = nif_erlang_system_time_1
@@ -1709,6 +1722,14 @@ term nif_erlang_make_ref_0(Context *ctx, int argc, term argv[])
17091722
return term_from_ref_ticks(ref_ticks, &ctx->heap);
17101723
}
17111724

1725+
static term nif_erlang_monotonic_time_0(Context *ctx, int argc, term argv[])
1726+
{
1727+
UNUSED(argc);
1728+
UNUSED(argv);
1729+
term native_argv[] = { NATIVE_ATOM };
1730+
return nif_erlang_monotonic_time_1(ctx, 1, native_argv);
1731+
}
1732+
17121733
term nif_erlang_monotonic_time_1(Context *ctx, int argc, term argv[])
17131734
{
17141735
UNUSED(ctx);
@@ -1734,6 +1755,14 @@ term nif_erlang_monotonic_time_1(Context *ctx, int argc, term argv[])
17341755
}
17351756
}
17361757

1758+
static term nif_erlang_system_time_0(Context *ctx, int argc, term argv[])
1759+
{
1760+
UNUSED(argc);
1761+
UNUSED(argv);
1762+
term native_argv[] = { NATIVE_ATOM };
1763+
return nif_erlang_system_time_1(ctx, 1, native_argv);
1764+
}
1765+
17371766
term nif_erlang_system_time_1(Context *ctx, int argc, term argv[])
17381767
{
17391768
UNUSED(ctx);

src/libAtomVM/nifs.gperf

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ binary:match/2, &binary_match_nif
4545
binary:match/3, &binary_match_nif
4646
calendar:system_time_to_universal_time/2, &system_time_to_universal_time_nif
4747
os:getenv/1, &os_getenv_nif
48+
os:system_time/0, &system_time_0_nif
49+
os:system_time/1, &system_time_nif
4850
erlang:atom_to_binary/1, &atom_to_binary_nif
4951
erlang:atom_to_binary/2, &atom_to_binary_nif
5052
erlang:atom_to_list/1, &atom_to_list_nif
@@ -101,7 +103,9 @@ erlang:system_flag/2, &system_flag_nif
101103
erlang:whereis/1, &whereis_nif
102104
erlang:++/2, &concat_nif
103105
erlang:--/2, &erlang_lists_subtract_nif
106+
erlang:monotonic_time/0, &monotonic_time_0_nif
104107
erlang:monotonic_time/1, &monotonic_time_nif
108+
erlang:system_time/0, &system_time_0_nif
105109
erlang:system_time/1, &system_time_nif
106110
erlang:tuple_to_list/1, &tuple_to_list_nif
107111
erlang:universaltime/0, &universaltime_nif

src/platforms/esp32/test/main/test_erl_sources/test_monotonic_time.erl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ test_monotonic_time_native() ->
5050
ok.
5151

5252
% Readings coarsest-to-finest: finer value is always taken later so
53-
% finer >= coarser * scale. Upper bound allows 1 ms between calls.
53+
% finer >= coarser * scale. Upper bound allows 1 s between calls.
5454
test_time_unit_ratios() ->
5555
S = erlang:monotonic_time(second),
5656
Ms = erlang:monotonic_time(millisecond),

src/platforms/rp2/tests/test_erl_sources/test_clocks.erl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ start() ->
6666
ok.
6767

6868
% Readings coarsest-to-finest: finer value is always taken later so
69-
% finer >= coarser * scale. Upper bound allows 1 ms between calls.
69+
% finer >= coarser * scale. Upper bound allows 1 s between calls.
7070
test_time_unit_ratios(Case, Fun) ->
7171
S = Fun(second),
7272
Ms = Fun(millisecond),

tests/erlang_tests/test_system_time.erl

Lines changed: 58 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,12 @@ start() ->
2626
ok = test_system_time(second, 1001),
2727
ok = test_system_time(millisecond, 10),
2828
ok = test_system_time(microsecond, 1),
29-
ok = test_system_time(nanosecond, 1),
29+
ok = test_nanosecond_system_time(),
3030
ok = test_native_system_time(),
3131

32+
ok = test_erlang_monotonic_time_0(),
33+
ok = test_erlang_system_time_0(),
34+
ok = test_os_system_time(),
3235
ok = test_time_unit_ratios(),
3336

3437
ok = expect(fun() -> erlang:system_time(not_a_time_unit) end, badarg),
@@ -44,31 +47,47 @@ test_system_time(Unit, SleepMs) ->
4447
true = (After > Before),
4548
ok.
4649

50+
test_erlang_monotonic_time_0() ->
51+
T = erlang:monotonic_time(),
52+
true = is_integer(T),
53+
ok.
54+
55+
test_erlang_system_time_0() ->
56+
T = erlang:system_time(),
57+
true = is_integer(T) andalso T > 0,
58+
ok.
59+
60+
test_os_system_time() ->
61+
T0 = os:system_time(),
62+
true = is_integer(T0) andalso T0 > 0,
63+
ok = test_system_time_unit(os_system_time_second, fun() -> os:system_time(second) end),
64+
ok = test_system_time_unit(os_system_time_millisecond, fun() -> os:system_time(millisecond) end),
65+
ok = test_system_time_unit(os_system_time_microsecond, fun() -> os:system_time(microsecond) end),
66+
ok = test_os_system_time_native(),
67+
ok.
68+
4769
test_system_time_unit(_Name, Fun) ->
4870
T = Fun(),
4971
true = is_integer(T) andalso T > 0,
5072
ok.
5173

5274
% Readings are taken coarsest-to-finest so the finer value is always >= the
53-
% coarser value * its scale factor. The upper-bound allows for at most 1 ms
75+
% coarser value * its scale factor. The upper-bound allows for at most 1 s
5476
% of wall-clock time between consecutive calls.
5577
test_time_unit_ratios() ->
5678
S = erlang:system_time(second),
5779
Ms = erlang:system_time(millisecond),
5880
Us = erlang:system_time(microsecond),
59-
Ns = erlang:system_time(nanosecond),
6081

6182
true = Ms >= S * 1000,
62-
% within 1 s of each other
83+
% within 1 s
6384
true = Ms < S * 1000 + 1000,
6485

6586
true = Us >= Ms * 1000,
66-
% within 1 ms
87+
% within 1 s
6788
true = Us < Ms * 1000 + 1000000,
6889

69-
true = Ns >= Us * 1000,
70-
% within 1 ms
71-
true = Ns < Us * 1000 + 1000000,
90+
ok = test_ns_ratio(),
7291

7392
ok.
7493

@@ -95,7 +114,7 @@ test_system_time_to_universal_time() ->
95114
{{1970, 1, 1}, {0, 0, 1}} = calendar:system_time_to_universal_time(1, second),
96115

97116
{{1970, 1, 1}, {0, 0, 0}} = calendar:system_time_to_universal_time(0, millisecond),
98-
{{1970, 1, 1}, {0, 0, 0}} = calendar:system_time_to_universal_time(0, millisecond),
117+
{{1970, 1, 1}, {0, 0, 0}} = calendar:system_time_to_universal_time(1, millisecond),
99118
{{1970, 1, 1}, {0, 0, 1}} = calendar:system_time_to_universal_time(1000, millisecond),
100119
{{1970, 1, 1}, {0, 0, 1}} = calendar:system_time_to_universal_time(1001, millisecond),
101120

@@ -108,27 +127,50 @@ test_system_time_to_universal_time() ->
108127

109128
{{1969, 12, 31}, {23, 59, 59}} = calendar:system_time_to_universal_time(-1, second),
110129

130+
ok = test_nanosecond_universal_time(),
131+
ok = test_native_universal_time(),
132+
133+
ok.
134+
135+
-if(?OTP_RELEASE >= 22).
136+
test_nanosecond_system_time() ->
137+
ok = test_system_time(nanosecond, 1).
138+
139+
test_native_system_time() ->
140+
ok = test_system_time(native, 1).
141+
142+
test_os_system_time_native() ->
143+
ok = test_system_time_unit(os_system_time_nanosecond, fun() -> os:system_time(nanosecond) end),
144+
ok = test_system_time_unit(os_system_time_native, fun() -> os:system_time(native) end),
145+
ok.
146+
147+
test_ns_ratio() ->
148+
Us = erlang:system_time(microsecond),
149+
Ns = erlang:system_time(nanosecond),
150+
true = Ns >= Us * 1000,
151+
% within 1 ms
152+
true = Ns < Us * 1000 + 1000000,
153+
ok.
154+
155+
test_nanosecond_universal_time() ->
111156
{{1970, 1, 1}, {0, 0, 0}} = calendar:system_time_to_universal_time(0, nanosecond),
112157
{{1970, 1, 1}, {0, 0, 0}} = calendar:system_time_to_universal_time(1, nanosecond),
113158
{{1970, 1, 1}, {0, 0, 1}} = calendar:system_time_to_universal_time(1000000000, nanosecond),
114159
{{1970, 1, 1}, {0, 0, 1}} = calendar:system_time_to_universal_time(1001000000, nanosecond),
115160
{{2023, 7, 8}, {20, 19, 39}} = calendar:system_time_to_universal_time(
116161
1688847579000000000, nanosecond
117162
),
118-
119-
ok = test_native_universal_time(),
120-
121163
ok.
122164

123-
-if(?OTP_RELEASE >= 22).
124-
test_native_system_time() ->
125-
ok = test_system_time(native, 1).
126-
127165
test_native_universal_time() ->
128166
{{1970, 1, 1}, {0, 0, 0}} = calendar:system_time_to_universal_time(0, native),
129167
{{1970, 1, 1}, {0, 0, 1}} = calendar:system_time_to_universal_time(1000000000, native),
130168
ok.
131169
-else.
170+
test_nanosecond_system_time() -> ok.
132171
test_native_system_time() -> ok.
172+
test_os_system_time_native() -> ok.
173+
test_ns_ratio() -> ok.
174+
test_nanosecond_universal_time() -> ok.
133175
test_native_universal_time() -> ok.
134176
-endif.

0 commit comments

Comments
 (0)