From 01febf830ac232cdcd0fde10e7ee32c7b404d7d0 Mon Sep 17 00:00:00 2001 From: Sam Clegg Date: Mon, 7 Mar 2022 09:05:35 -0800 Subject: [PATCH] Move more time functions to native code. NFC I'm pretty this is going to be codesize win for a lot of codebases even though its not showing up in our (minimal) codesize tests. --- src/library.js | 98 ++++--------------------------- src/library_wasi.js | 8 +-- src/struct_info.json | 2 - system/lib/libc/emscripten_time.c | 91 ++++++++++++++++++++++++++++ tests/reference_struct_info.json | 2 - tools/system_libs.py | 3 +- 6 files changed, 109 insertions(+), 95 deletions(-) diff --git a/src/library.js b/src/library.js index 33cd1392e84cf..689e812773bb0 100644 --- a/src/library.js +++ b/src/library.js @@ -391,25 +391,9 @@ LibraryManager.library = { // time.h // ========================================================================== - clock__sig: 'i', - clock: function() { - if (_clock.start === undefined) _clock.start = Date.now(); - return ((Date.now() - _clock.start) * ({{{ cDefine('CLOCKS_PER_SEC') }}} / 1000))|0; - }, - - time__sig: 'ii', - time: function(ptr) { - {{{ from64('ptr') }}}; - var ret = (Date.now()/1000)|0; - if (ptr) { - {{{ makeSetValue('ptr', 0, 'ret', 'i32') }}}; - } - return ret; - }, - - difftime__sig: 'dii', - difftime: function(time1, time0) { - return time1 - time0; + _emscripten_date_now__sig: 'j', + _emscripten_date_now: function() { + return Date.now(); }, _mktime_js__sig: 'ii', @@ -549,11 +533,6 @@ LibraryManager.library = { return ret; }, - dysize: function(year) { - var leap = ((year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0))); - return leap ? 366 : 365; - }, - // TODO: Initialize these to defaults on startup from system settings. // Note: glibc has one fewer underscore for all of these. Also used in other related functions (timegm) _tzset_js__deps: ['tzset_impl'], @@ -1246,66 +1225,6 @@ LibraryManager.library = { return _strptime(buf, format, tm); // no locale support yet }, - timespec_get__deps: ['clock_gettime', '$setErrNo'], - timespec_get: function(ts, base) { - //int timespec_get(struct timespec *ts, int base); - if (base !== {{{ cDefine('TIME_UTC') }}}) { - // There is no other implemented value than TIME_UTC; all other values are considered erroneous. - setErrNo({{{ cDefine('EINVAL') }}}); - return 0; - } - var ret = _clock_gettime({{{ cDefine('CLOCK_REALTIME') }}}, ts); - return ret < 0 ? 0 : base; - }, - - // ========================================================================== - // sys/time.h - // ========================================================================== - - clock_gettime__sig: 'iii', - clock_gettime__deps: ['emscripten_get_now', 'emscripten_get_now_is_monotonic', '$setErrNo'], - clock_gettime: function(clk_id, tp) { - // int clock_gettime(clockid_t clk_id, struct timespec *tp); - var now; - if (clk_id === {{{ cDefine('CLOCK_REALTIME') }}}) { - now = Date.now(); - } else if ((clk_id === {{{ cDefine('CLOCK_MONOTONIC') }}} || clk_id === {{{ cDefine('CLOCK_MONOTONIC_RAW') }}}) && _emscripten_get_now_is_monotonic) { - now = _emscripten_get_now(); - } else { - setErrNo({{{ cDefine('EINVAL') }}}); - return -1; - } - {{{ makeSetValue('tp', C_STRUCTS.timespec.tv_sec, '(now/1000)|0', 'i32') }}}; // seconds - {{{ makeSetValue('tp', C_STRUCTS.timespec.tv_nsec, '((now % 1000)*1000*1000)|0', 'i32') }}}; // nanoseconds - return 0; - }, - __clock_gettime__sig: 'iii', - __clock_gettime: 'clock_gettime', // musl internal alias - clock_getres__deps: ['emscripten_get_now_res', 'emscripten_get_now_is_monotonic', '$setErrNo'], - clock_getres: function(clk_id, res) { - // int clock_getres(clockid_t clk_id, struct timespec *res); - var nsec; - if (clk_id === {{{ cDefine('CLOCK_REALTIME') }}}) { - nsec = 1000 * 1000; // educated guess that it's milliseconds - } else if (clk_id === {{{ cDefine('CLOCK_MONOTONIC') }}} && _emscripten_get_now_is_monotonic) { - nsec = _emscripten_get_now_res(); - } else { - setErrNo({{{ cDefine('EINVAL') }}}); - return -1; - } - {{{ makeSetValue('res', C_STRUCTS.timespec.tv_sec, '(nsec/1000000000)|0', 'i32') }}}; - {{{ makeSetValue('res', C_STRUCTS.timespec.tv_nsec, 'nsec', 'i32') }}} // resolution is nanoseconds - return 0; - }, - gettimeofday__sig: 'iii', - // http://pubs.opengroup.org/onlinepubs/000095399/basedefs/sys/time.h.html - gettimeofday: function(ptr) { - var now = Date.now(); - {{{ makeSetValue('ptr', C_STRUCTS.timeval.tv_sec, '(now/1000)|0', 'i32') }}}; // seconds - {{{ makeSetValue('ptr', C_STRUCTS.timeval.tv_usec, '((now % 1000)*1000)|0', 'i32') }}}; // microseconds - return 0; - }, - // ========================================================================== // sys/timeb.h // ========================================================================== @@ -2463,8 +2382,9 @@ LibraryManager.library = { // Represents whether emscripten_get_now is guaranteed monotonic; the Date.now // implementation is not :( + $nowIsMonotonic__internal: true, #if MIN_IE_VERSION <= 9 || MIN_FIREFOX_VERSION <= 14 || MIN_CHROME_VERSION <= 23 || MIN_SAFARI_VERSION <= 80400 // https://caniuse.com/#feat=high-resolution-time - emscripten_get_now_is_monotonic: ` + $nowIsMonotonic: ` ((typeof performance == 'object' && performance && typeof performance['now'] == 'function') #if ENVIRONMENT_MAY_BE_NODE || ENVIRONMENT_IS_NODE @@ -2475,9 +2395,15 @@ LibraryManager.library = { );`, #else // Modern environment where performance.now() is supported: (rely on minifier to return true unconditionally from this function) - emscripten_get_now_is_monotonic: 'true;', + $nowIsMonotonic: 'true;', #endif + _emscripten_get_now_is_monotonic__internal: true, + _emscripten_get_now_is_monotonic__deps: ['$nowIsMonotonic'], + _emscripten_get_now_is_monotonic: function() { + return nowIsMonotonic; + }, + #if MINIMAL_RUNTIME $warnOnce: function(text) { if (!warnOnce.shown) warnOnce.shown = {}; diff --git a/src/library_wasi.js b/src/library_wasi.js index d91a7f563c0da..ebed03138d7f6 100644 --- a/src/library_wasi.js +++ b/src/library_wasi.js @@ -137,7 +137,7 @@ var WasiLibrary = { // either wait for BigInt support or to legalize on the client. clock_time_get__nothrow: true, clock_time_get__sig: 'iiiii', - clock_time_get__deps: ['emscripten_get_now', 'emscripten_get_now_is_monotonic', '$checkWasiClock'], + clock_time_get__deps: ['emscripten_get_now', '$nowIsMonotonic', '$checkWasiClock'], clock_time_get: function(clk_id, {{{ defineI64Param('precision') }}}, ptime) { {{{ receiveI64ParamAsI32s('precision') }}} if (!checkWasiClock(clk_id)) { @@ -147,7 +147,7 @@ var WasiLibrary = { // all wasi clocks but realtime are monotonic if (clk_id === {{{ cDefine('__WASI_CLOCKID_REALTIME') }}}) { now = Date.now(); - } else if (_emscripten_get_now_is_monotonic) { + } else if (nowIsMonotonic) { now = _emscripten_get_now(); } else { return {{{ cDefine('ENOSYS') }}}; @@ -161,7 +161,7 @@ var WasiLibrary = { clock_res_get__nothrow: true, clock_res_get__sig: 'iii', - clock_res_get__deps: ['emscripten_get_now', 'emscripten_get_now_res', 'emscripten_get_now_is_monotonic', '$checkWasiClock'], + clock_res_get__deps: ['emscripten_get_now', 'emscripten_get_now_res', '$nowIsMonotonic', '$checkWasiClock'], clock_res_get: function(clk_id, pres) { if (!checkWasiClock(clk_id)) { return {{{ cDefine('EINVAL') }}}; @@ -170,7 +170,7 @@ var WasiLibrary = { // all wasi clocks but realtime are monotonic if (clk_id === {{{ cDefine('CLOCK_REALTIME') }}}) { nsec = 1000 * 1000; // educated guess that it's milliseconds - } else if (_emscripten_get_now_is_monotonic) { + } else if (nowIsMonotonic) { nsec = _emscripten_get_now_res(); } else { return {{{ cDefine('ENOSYS') }}}; diff --git a/src/struct_info.json b/src/struct_info.json index 0354af4895051..24490c16aa2af 100644 --- a/src/struct_info.json +++ b/src/struct_info.json @@ -158,8 +158,6 @@ { "file": "time.h", "defines": [ - ["li", "CLOCKS_PER_SEC"], - "TIME_UTC", "CLOCK_REALTIME", "CLOCK_MONOTONIC", "CLOCK_MONOTONIC_RAW" diff --git a/system/lib/libc/emscripten_time.c b/system/lib/libc/emscripten_time.c index 8c2bc24ed9334..6f150c1229dfd 100644 --- a/system/lib/libc/emscripten_time.c +++ b/system/lib/libc/emscripten_time.c @@ -5,8 +5,11 @@ * found in the LICENSE file. */ +#include #include #include +#include +#include #include "libc.h" // Replaces musl's __tz.c @@ -20,6 +23,8 @@ time_t _timegm_js(struct tm *tm); time_t _mktime_js(struct tm *tm); void _localtime_js(const time_t *restrict t, struct tm *restrict tm); void _gmtime_js(const time_t *restrict t, struct tm *restrict tm); +double _emscripten_date_now(); +double emscripten_get_now_res(); __attribute__((__weak__)) void tzset() { @@ -57,5 +62,91 @@ struct tm *__gmtime_r(const time_t *restrict t, struct tm *restrict tm) { return tm; } +__attribute__((__weak__)) +clock_t __clock() { + static thread_local double start = 0; + if (!start) { + start = _emscripten_date_now(); + } + return (_emscripten_date_now() - start) * (CLOCKS_PER_SEC / 1000); +} + +__attribute__((__weak__)) +time_t __time(time_t *t) { + double ret = _emscripten_date_now() / 1000; + if (t) { + *t = ret; + } + return ret; +} + +extern bool _emscripten_get_now_is_monotonic(); +static thread_local bool checked_monotonic = false; +static thread_local bool is_monotonic = 0; + +__attribute__((__weak__)) +int __clock_gettime(clockid_t clk, struct timespec *ts) { + if (!checked_monotonic) { + is_monotonic = _emscripten_get_now_is_monotonic(); + checked_monotonic = true; + } + + double now_ms; + if (clk == CLOCK_REALTIME) { + now_ms = _emscripten_date_now(); + } else if ((clk == CLOCK_MONOTONIC || clk == CLOCK_MONOTONIC_RAW) && is_monotonic) { + now_ms = emscripten_get_now(); + } else { + errno = EINVAL; + return -1; + } + + long long now_s = now_ms / 1000; + ts->tv_sec = now_s; // seconds + ts->tv_nsec = (now_ms - (now_s * 1000)) * 1000 * 1000; // nanoseconds + return 0; +} + +__attribute__((__weak__)) +int __clock_getres(clockid_t clk, struct timespec *ts) { + if (!checked_monotonic) { + is_monotonic = _emscripten_get_now_is_monotonic(); + checked_monotonic = true; + } + + double nsec; + if (clk == CLOCK_REALTIME) { + nsec = 1000 * 1000; // educated guess that it's milliseconds + } else if (clk == CLOCK_MONOTONIC && is_monotonic) { + nsec = emscripten_get_now_res(); + } else { + errno = EINVAL; + return -1; + } + ts->tv_sec = (nsec / (1000 * 1000 * 1000)); + ts->tv_nsec = nsec; + return 0; +} + +__attribute__((__weak__)) +int __gettimeofday(struct timeval *restrict tv, void *restrict tz) { + double now_ms = _emscripten_date_now(); + long long now_s = now_ms / 1000; + tv->tv_sec = now_s; // seconds + tv->tv_usec = (now_ms - (now_s * 1000)) * 1000; // nicroseconds + return 0; +} + +__attribute__((__weak__)) +int dysize(int year) { + int leap = ((year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0))); + return leap ? 366 : 365; +} + weak_alias(__gmtime_r, gmtime_r); weak_alias(__localtime_r, localtime_r); +weak_alias(__time, time); +weak_alias(__clock, clock); +weak_alias(__clock_gettime, clock_gettime); +weak_alias(__clock_getres, clock_getres); +weak_alias(__gettimeofday, gettimeofday); diff --git a/tests/reference_struct_info.json b/tests/reference_struct_info.json index bd34841d95e7f..bcda9b293bc85 100644 --- a/tests/reference_struct_info.json +++ b/tests/reference_struct_info.json @@ -17,7 +17,6 @@ "AUDIO_F32": 33056, "AUDIO_S16LSB": 32784, "AUDIO_U8": 8, - "CLOCKS_PER_SEC": 1000000, "CLOCK_MONOTONIC": 1, "CLOCK_MONOTONIC_RAW": 4, "CLOCK_REALTIME": 0, @@ -351,7 +350,6 @@ "TCSETS": 21506, "TCSETSF": 21508, "TCSETSW": 21507, - "TIME_UTC": 1, "TIOCGPGRP": 21519, "TIOCGWINSZ": 21523, "TIOCSPGRP": 21520, diff --git a/tools/system_libs.py b/tools/system_libs.py index ab50a52eada00..d8d9061f50450 100644 --- a/tools/system_libs.py +++ b/tools/system_libs.py @@ -915,11 +915,13 @@ def get_files(self): 'asctime_r.c', 'asctime.c', 'ctime.c', + 'difftime.c', 'gmtime.c', 'localtime.c', 'nanosleep.c', 'clock_nanosleep.c', 'ctime_r.c', + 'timespec_get.c', 'utime.c', ]) libc_files += files_in_path( @@ -1644,7 +1646,6 @@ def get_files(self): '__year_to_secs.c', 'clock.c', 'clock_gettime.c', - 'difftime.c', 'gettimeofday.c', 'localtime_r.c', 'gmtime_r.c',