Skip to content

Commit

Permalink
Move more time functions to native code. NFC (#16439)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
sbc100 authored Mar 9, 2022
1 parent 504f4e0 commit fa6afb8
Show file tree
Hide file tree
Showing 7 changed files with 113 additions and 99 deletions.
98 changes: 12 additions & 86 deletions src/library.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down Expand Up @@ -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'],
Expand Down Expand Up @@ -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
// ==========================================================================
Expand Down Expand Up @@ -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
Expand All @@ -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 = {};
Expand Down
8 changes: 4 additions & 4 deletions src/library_wasi.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand All @@ -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') }}};
Expand All @@ -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') }}};
Expand All @@ -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') }}};
Expand Down
2 changes: 0 additions & 2 deletions src/struct_info.json
Original file line number Diff line number Diff line change
Expand Up @@ -158,8 +158,6 @@
{
"file": "time.h",
"defines": [
["li", "CLOCKS_PER_SEC"],
"TIME_UTC",
"CLOCK_REALTIME",
"CLOCK_MONOTONIC",
"CLOCK_MONOTONIC_RAW"
Expand Down
91 changes: 91 additions & 0 deletions system/lib/libc/emscripten_time.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@
* found in the LICENSE file.
*/

#include <emscripten/emscripten.h>
#include <time.h>
#include <stdbool.h>
#include <sys/time.h>
#include <threads.h>
#include "libc.h"

// Replaces musl's __tz.c
Expand All @@ -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() {
Expand Down Expand Up @@ -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);
8 changes: 4 additions & 4 deletions tests/code_size/random_printf_wasm.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"a.html": 12955,
"a.html.gz": 6995,
"total": 12955,
"total_gz": 6995
"a.html": 12977,
"a.html.gz": 6991,
"total": 12977,
"total_gz": 6991
}
2 changes: 0 additions & 2 deletions tests/reference_struct_info.json
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -351,7 +350,6 @@
"TCSETS": 21506,
"TCSETSF": 21508,
"TCSETSW": 21507,
"TIME_UTC": 1,
"TIOCGPGRP": 21519,
"TIOCGWINSZ": 21523,
"TIOCSPGRP": 21520,
Expand Down
3 changes: 2 additions & 1 deletion tools/system_libs.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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',
Expand Down

0 comments on commit fa6afb8

Please sign in to comment.