package erjang.m.erlang; import erjang.*; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; import java.util.function.BinaryOperator; /** * Created by krab on 26/04/16. */ public class ErlTime { static final EAtom am_native = EAtom.intern("native"); static final EAtom am_nano_seconds = EAtom.intern("nano_seconds"); static final EAtom am_micro_seconds = EAtom.intern("micro_seconds"); static final EAtom am_milli_seconds = EAtom.intern("milli_seconds"); static final EAtom am_seconds = EAtom.intern("seconds"); // native time is nano @BIF(name = "now") static public ETuple3 now() { long now = now_unique_micros(); int micros = (int)(now % 1000000); now /= 1000000; int secs = (int)(now % 1000000); now /= 1000000; int megas = (int)now; ETuple3 res = new ETuple3(); res.elem1 = ERT.box(megas); res.elem2 = ERT.box(secs); res.elem3 = ERT.box(micros); return res; } final static AtomicLong latest_now = new AtomicLong(); final static long micros_from_epoch_to_nanotime = System.currentTimeMillis() * 1000 - System.nanoTime() / 1000; public static long now_raw_micros() { return System.nanoTime() / 1000 + micros_from_epoch_to_nanotime; } static long now_unique_micros() { /* now() must fulfill: * - Any return value approximates the current time. * - The return values are strictly increasing (and thus unique). * We ensure the latter by (a) always increasing latest_now, * (b) always returning what we set it to. */ long micros = now_raw_micros(); long prev; while ((prev = latest_now.get()) < micros) { if (latest_now.compareAndSet(prev,micros)) { return micros; } } return latest_now.incrementAndGet(); } static long now_monotonic_micros() { /* now() must fulfill: * - Any return value approximates the current time. * - The return values are monotonic */ long micros = now_raw_micros(); long prev; while ((prev = latest_now.get()) <= micros) { if (prev == micros || latest_now.compareAndSet(prev,micros)) { return micros; } } return latest_now.get(); } @BIF public static ENumber monotonic_time() { return ERT.box(now_monotonic_micros()); } @BIF public static EInteger monotonic_time(EObject units) { return convert_time_unit(monotonic_time(), am_native, units); } // return current time in micros @BIF public static EInteger system_time() { return ERT.box(now_raw_micros()); } @BIF public static EInteger system_time(EObject units) { return convert_time_unit(system_time(), am_native, units); } @BIF public static EInteger convert_time_unit(EObject time0, EObject from, EObject to) { EInteger time = time0.testInteger(); if (time == null) { throw ERT.badarg(time0, from, to); } int fu = integer_time_unit(from); int tu = integer_time_unit(to); if (time.is_lt(ESmall.ZERO)) { return time.r_multiply(tu).subtract(fu-1).idiv(fu); } else { return time.r_multiply(tu).idiv(fu); } } private static int integer_time_unit(EObject unit) { if (unit == am_nano_seconds) { return 1000 * 1000 * 1000; } else if (unit == am_micro_seconds || unit == am_native) { return 1000 * 1000; } else if (unit == am_milli_seconds) { return 1000; } else if (unit == am_seconds) { return 1; } else { ESmall iu = unit.testSmall(); if (iu == null) { throw new ErlangError(EAtom.intern("bad_time_unit"), ERT.NIL.cons(unit)); } return iu.value; } } static AtomicReference<EInteger> uniqueValue = new AtomicReference<EInteger>(ESmall.ONE); @BIF public static EInteger unique_integer() { return uniqueValue.getAndAccumulate(ESmall.ONE, new BinaryOperator<EInteger>() { @Override public EInteger apply(EInteger i1, EInteger i2) { return (EInteger)i1.add(i2); } }); } @BIF public static EInteger unique_integer(EObject opts) { return unique_integer(); } }