Fuzion Logo
fuzion-lang.dev — The Fuzion Language Portal
JavaScript seems to be disabled. Functionality is limited.

time/Clock.fz


# This file is part of the Fuzion language implementation.
#
# The Fuzion language implementation is free software: you can redistribute it
# and/or modify it under the terms of the GNU General Public License as published
# by the Free Software Foundation, version 3 of the License.
#
# The Fuzion language implementation is distributed in the hope that it will be
# useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
# License for more details.
#
# You should have received a copy of the GNU General Public License along with The
# Fuzion language implementation.  If not, see <https://www.gnu.org/licenses/>.


# -----------------------------------------------------------------------
#
#  Tokiwa Software GmbH, Germany
#
#  Source code of Fuzion standard library feature time.Clock
#
#  Author: Fridtjof Siebert (siebert@tokiwa.software)
#
# -----------------------------------------------------------------------

# time.Clock -- class of effects that implement a clock
#
# This is a `ref` feature such that code that requires an arbitrary `Clock`
# effect could be written in a way that permits its execution using different
# actual `Clock` implementations like `time.nano` that provides high
# precision with low accuracy or a different clock that provides higher accuracy.
#
public Clock ref : effect is

  # read the time of this clock
  #
  public read time.instant => abstract


  # halt the current thread until the given instant. In case `t` is in the past,
  # return immediately.
  #
  public sleep_until(# the instant we should wait for
                     t time.instant
                     ) unit
  =>
    sleep_until t read


  # internal helper to implement `sleep_until` if the current time has already been
  # read and is passed as `just_now`.
  #
  sleep_until(# the instant we should wait for
              t,

              # the current instant that has just been `read`.
              just_now time.instant)
  =>
    if just_now < t
      sleep t-just_now

  # halt the current thread during the given period of time
  #
  public sleep(d time.duration) unit => abstract


  # default implementation of Clock using default_nano_time.
  #
  # this will get instated automatically at startup
  #
  public redef fixed type.default_value option time.Clock =>
    nano.default_value.bind (x -> id time.Clock x)


  # run the given code once at the provided instant within the current thread
  #
  # In case the instant is in the past, do not run the code. In case the instant is
  # in the future, wait until this clock reached the time specified by instant.
  #
  # return true if the code was actually run, false if the given time `t` is already
  # in the past.
  #
  public run_once(t instant,
                  code ()->unit) bool
  =>
    abstract


  # run the given `code` periodically at time `first+p*0`, `first+p*1`, `first+p*2`,
  # etc. until this clock has reached the time `last`.
  #
  # In case any of the instants `first+p*0`, `first+p*1`, `first+p*2`, etc. happen to
  # be in the past when this is called or after the previous execution of `code`,
  # respectively, do not run `code` for that instant.
  #
  # In case `period.is_infinity`, run `code` once unless `first > last`.
  #
  # return the number of times the `code` was run.
  #
  public run_periodic(P type : time.Period,
                      first time.instant,
                      period P,
                      last time.instant,
                      code ()->unit) u64
  pre
    debug: period.is_infinity || period.as_duration > time.duration.ns 0
  post
    debug: ((last >= first): result <= ((last - first) / period.as_duration) + 1)
    debug: ((last <  first): result = 0)
  =>
    for
      cnt := u64 0, cnt + 1
      just_now := read
      next1 := first + period * cnt
      skipped := if just_now > next1 then (just_now - next1) / period.as_duration + 1
                                     else 0
      next := first + period * (cnt + skipped)
    while next <= last
      sleep_until next just_now
      code()
    else
      cnt

last changed: 2026-05-12