import time from abc import ABCMeta, abstractmethod from typing import Union from ..lock_helper import LockContext, LockContextType class BaseTime(metaclass=ABCMeta): """ Overview: Abstract time interface Interfaces: ``time`` """ @abstractmethod def time(self) -> Union[int, float]: """ Overview: Get time information Returns: - time(:obj:`float, int`): time information """ raise NotImplementedError class NaturalTime(BaseTime): """ Overview: Natural time object Interfaces: ``__init__``, ``time`` Example: >>> from ding.utils.autolog.time_ctl import NaturalTime >>> time_ = NaturalTime() """ def __init__(self): self.__last_time = None def time(self) -> float: """ Overview: Get current natural time (float format, unix timestamp) Returns: - time(:obj:`float`): unix timestamp Example: >>> from ding.utils.autolog.time_ctl import NaturalTime >>> time_ = NaturalTime() >>> time_.time() 1603896383.8811457 """ _current_time = time.time() if self.__last_time is not None: _current_time = max(_current_time, self.__last_time) self.__last_time = _current_time return _current_time class TickTime(BaseTime): """ Overview: Tick time object Interfaces: ``__init__``, ``step``, ``time`` Example: >>> from ding.utils.autolog.time_ctl import TickTime >>> time_ = TickTime() """ def __init__(self, init: int = 0): """ Overview: Constructor of TickTime Arguments: - init (:obj:`int`): initial time, default is 0 """ self.__tick_time = init def step(self, delta: int = 1) -> int: """ Overview Step the time forward for this TickTime Arguments: - delta (:obj:`int`): steps to step forward, default is 1 Returns: - time (:obj:`int`): new time after stepping Example: >>> from ding.utils.autolog.time_ctl import TickTime >>> time_ = TickTime(0) >>> time_.step() 1 >>> time_.step(2) 3 """ if not isinstance(delta, int): raise TypeError("Delta should be positive int, but {actual} found.".format(actual=type(delta).__name__)) elif delta < 1: raise ValueError("Delta should be no less than 1, but {actual} found.".format(actual=repr(delta))) else: self.__tick_time += delta return self.__tick_time def time(self) -> int: """ Overview Get current tick time Returns: int: current tick time Example: >>> from ding.utils.autolog.time_ctl import TickTime >>> time_ = TickTime(0) >>> time_.step() >>> time_.time() 1 """ return self.__tick_time class TimeProxy(BaseTime): """ Overview: Proxy of time object, it can freeze time, sometimes useful when reproducing. This object is thread-safe, and also freeze and unfreeze operation is strictly ordered. Interfaces: ``__init__``, ``freeze``, ``unfreeze``, ``time``, ``current_time`` Example: >>> from ding.utils.autolog.time_ctl import TickTime, TimeProxy >>> tick_time_ = TickTime() >>> time_ = TimeProxy(tick_time_) >>> tick_time_.step() >>> print(tick_time_.time(), time_.time(), time_.current_time()) 1 1 1 >>> time_.freeze() >>> tick_time_.step() >>> print(tick_time_.time(), time_.time(), time_.current_time()) 2 1 2 >>> time_.unfreeze() >>> print(tick_time_.time(), time_.time(), time_.current_time()) 2 2 2 """ def __init__(self, time_: BaseTime, frozen: bool = False, lock_type: LockContextType = LockContextType.THREAD_LOCK): """ Overview: Constructor for Time proxy Arguments: - time_ (:obj:`BaseTime`): another time object it based on - frozen (:obj:`bool`): this object will be frozen immediately if true, otherwise not, default is False - lock_type (:obj:`LockContextType`): type of the lock, default is THREAD_LOCK """ self.__time = time_ self.__current_time = self.__time.time() self.__frozen = frozen self.__lock = LockContext(lock_type) self.__frozen_lock = LockContext(lock_type) if self.__frozen: self.__frozen_lock.acquire() @property def is_frozen(self) -> bool: """ Overview: Get if this time proxy object is frozen Returns: bool: true if it is frozen, otherwise false """ with self.__lock: return self.__frozen def freeze(self): """ Overview: Freeze this time proxy """ with self.__lock: self.__frozen_lock.acquire() self.__frozen = True self.__current_time = self.__time.time() def unfreeze(self): """ Overview: Unfreeze this time proxy """ with self.__lock: self.__frozen = False self.__frozen_lock.release() def time(self) -> Union[int, float]: """ Overview: Get time (may be frozen time) Returns: int or float: the time """ with self.__lock: if self.__frozen: return self.__current_time else: return self.__time.time() def current_time(self) -> Union[int, float]: """ Overview: Get current time (will not be frozen time) Returns: int or float: current time """ return self.__time.time()