上一篇的源码看得十分无趣,官方文档跟黑心棉一样渣。

  这一篇讲讲windows操作系统上的时间戳实现,由于类的声明,方法解释上一篇都贴过了,所以这次直接上对应版本的代码。

  windows与mac很不一样,实现了一个新的Clock类来管理时间,如下。

// We implement time using the high-resolution timers so that we can get
// timeouts which are smaller than 10-15ms. To avoid any drift, we
// periodically resync the internal clock to the system clock.
class Clock final {
public:
Clock() : initial_ticks_(GetSystemTicks()), initial_time_(GetSystemTime()) {} Time Now() { /* */ } Time NowFromSystemTime() { /* */ } private:
static TimeTicks GetSystemTicks() { /* */ } static Time GetSystemTime() { /* */ } TimeTicks initial_ticks_;
Time initial_time_;
Mutex mutex_;
};

  从注释和方法名可以看出,windows完全用这个新类代替了老的Time、TimeTicks,因为这个方法拥有更好的性能,这个类同时会周期性的与系统时间同步数据。

  下面正式开始。

  先从Now方法看起,看windows系统是如何获取本地的时间戳。

DEFINE_LAZY_LEAKY_OBJECT_GETTER(Clock, GetClock)

#define DEFINE_LAZY_LEAKY_OBJECT_GETTER(T, FunctionName, ...) \
T* FunctionName() { \
static ::v8::base::LeakyObject<T> object{__VA_ARGS__}; \
return object.get(); \
} Time Time::Now() { return GetClock()->Now(); }

  这个方法的定义也不一般,直接用了一个特殊宏,宏就不展开了,简单说就是懒加载,调用的时候会分配空间生成一个Clock类,初始化完后第二次调用就直接返回了,当成一个单例来理解。

  直接看宏的返回类型,刚好是上面的Clock,该类只有一个无参构造函数,初始化两个时间戳属性。

  先看后那个,也就是系统时间的时间戳。

static Time GetSystemTime() {
FILETIME ft;
::GetSystemTimeAsFileTime(&ft);
return Time::FromFiletime(ft);
}

  这里的FILETIME和GetSystemTimeAsFileTime都是windowsAPI,可以获取当前系统的日期和时间,但是返回值很奇怪。

typedef struct _FILETIME {
DWORD dwLowDateTime;
DWORD dwHighDateTime;
} FILETIME, *PFILETIME, *LPFILETIME;

Contains a 64-bit value representing the number of 100-nanosecond intervals since January 1, 1601 (UTC).

  这是结构体的声明与解释,High、Low分别代表时间的高位与地位,而那个方法就是配合这个使用的。

  可以从上面看到,这个API返回的时间竟然是从1601年1月1日开始算的,不知道那一年发生了什么。

  下面写一个测试代码。

int main()
{
FILETIME ft;
LARGE_INTEGER t;
::GetSystemTimeAsFileTime(&ft);
t.LowPart = ft.dwLowDateTime;
t.HighPart = ft.dwHighDateTime;
cout << t.QuadPart << endl;
}

  得到的输出为132034487665022709,由于单位是100纳秒,所以这个数字乘以100的,然后换算一下。

  由于基准是1601年,而Date是从1970年开始算,所以年份上差了369年,刚好是2019,很合理。

  来看看V8的处理。

// Time between windows epoch and standard epoch.
static const int64_t kTimeToEpochInMicroseconds = int64_t{}; Time Time::FromFiletime(FILETIME ft) {
// 特殊情况处理
if (ft.dwLowDateTime == && ft.dwHighDateTime == ) {
return Time();
}
if (ft.dwLowDateTime == std::numeric_limits<DWORD>::max() &&
ft.dwHighDateTime == std::numeric_limits<DWORD>::max()) {
return Max();
}
// 换算
int64_t us = (static_cast<uint64_t>(ft.dwLowDateTime) +
(static_cast<uint64_t>(ft.dwHighDateTime) << )) / ;
return Time(us - kTimeToEpochInMicroseconds);
}

  前面的特殊情况看看就行了,主要是换算这一步,就是简单的将高低位的数值拼到了一起,除以10之后,单位从100纳秒变成了微秒。

  最后的计算,也是为了平衡标准的时间戳和windows时间戳两者的差异,如下。

  为什么不是1970 - 1601 = 369年整呢?因为中间有闰年,很合理。

  最后得到微秒单位的标准时间戳,将该数值赋到类的属性上。

  回到最初的Now方法,初始化完后,会调用Clock自身的Now方法获取最终的时间戳,如下。

Time Now() {
// 一个误差临界值
const TimeDelta kMaxElapsedTime = TimeDelta::FromMinutes(); // 我目前不想解析所有关于锁的东西
MutexGuard lock_guard(&mutex_); // 再次获取当前的硬件时间戳与本地时间戳
TimeTicks ticks = GetSystemTicks();
Time time = GetSystemTime(); // 这里进行误差修正
TimeDelta elapsed = ticks - initial_ticks_;
// 1.当前时间小于初始化时间 可参考上一篇中类方法的注释(the system might adjust its clock...)
// 2.硬件时间戳的时间差超过临界值 这种情况基本可以认定初始化的时间完全不可信了
if (time < initial_time_ || elapsed > kMaxElapsedTime) {
initial_ticks_ = ticks;
initial_time_ = time;
return time;
} return initial_time_ + elapsed;
}

  虽然在构造函数中获取了时间戳,但是V8考虑到由于函数调用、系统修正等原因导致的误差(比如第一次初始化),再次进行了修正,具体操作和原因可以直接看注释,最后返回的时间戳是计算获得的理论本地时间戳加上硬件时间戳差值。

  至于NewFromSystemTime就比较简单了,在mac中这两个方法是一个,在windows里如下。

Time NowFromSystemTime() {
MutexGuard lock_guard(&mutex_);
// 更新两个时间戳
initial_ticks_ = GetSystemTicks();
initial_time_ = GetSystemTime();
// 直接返回最新获得的时间戳
return initial_time_;
}

  不计算任何东西,直接返回系统API的时间戳,可以配合注释来理解这两个方法。  

  尴尬了,没想到V8在Time阶段把两个时间戳全用上了。稍微看了一下TimeTicks的实现,发现还有点意思,所以这一篇先这样了,太长了写的累。

深入V8引擎-Time核心方法之win篇(1)的更多相关文章

  1. 深入V8引擎-Time核心方法之win篇(2)

    这一篇讲windows系统下TimeTicks的实现. 对于tick,V8写了相当长的一段discussion来讨论windows系统上计数的三种实现方法以及各自的优劣,注释在time.cc的572行 ...

  2. 深入V8引擎-Time核心方法之mac篇

    由于底层逻辑实现不同操作系统区别很大,所以干脆分篇来说. 主要讲一下Time.TimeTicks两个类里面对于时间戳的实现,其余的运算符重载.边缘工具方法就不看了,先是Time. Time 类本身的说 ...

  3. 浅谈Chrome V8引擎中的垃圾回收机制

    垃圾回收器 JavaScript的垃圾回收器 JavaScript使用垃圾回收机制来自动管理内存.垃圾回收是一把双刃剑,其好处是可以大幅简化程序的内存管理代码,降低程序员的负担,减少因 长时间运转而带 ...

  4. 浅谈V8引擎中的垃圾回收机制

    最近在看<深入浅出nodejs>关于V8垃圾回收机制的章节,转自:http://blog.segmentfault.com/skyinlayer/1190000000440270 这篇文章 ...

  5. 深入出不来nodejs源码-V8引擎初探

    原本打算是把node源码看得差不多了再去深入V8的,但是这两者基本上没办法分开讲. 与express是基于node的封装不同,node是基于V8的一个应用,源码内容已经渗透到V8层面,因此这章简述一下 ...

  6. Chrome V8引擎系列随笔 (1):Math.Random()函数概览

    先让大家来看一幅图,这幅图是V8引擎4.7版本和4.9版本Math.Random()函数的值的分布图,我可以这么理解 .从下图中,也许你会认为这是个二维码?其实这幅图告诉我们一个道理,第二张图的点的分 ...

  7. (译)V8引擎介绍

    V8是什么? V8是谷歌在德国研发中心开发的一个JavaScript引擎.开源并且用C++实现.可以用于运行于客户端和服务端的Javascript程序. V8设计的初衷是为了提高浏览器上JavaScr ...

  8. V8引擎嵌入指南

    如果已读过V8编程入门那你已经熟悉了如句柄(handle).作用域(scope)和上下文(context)之类的关键概念,以及如何将V8引擎作为一个独立的虚拟机来使用.本文将进一步讨论这些概念,并介绍 ...

  9. EF5+MVC4系列(9) Razor视图引擎的核心原理;@符号的使用;输出html的转义

    一:Razor视图引擎的核心原理 Razor是ASP.NET MVC 3中新加入的技术,以作为ASPX引擎的一个新的替代项 ,他是一个视图引擎 他的核心原理,就是当读取到 @符号的时候,就认为这是开始 ...

随机推荐

  1. Python linecache模块

    Table of Contents 1. linecache 1.1. 其它 2. 参考资料 linecache 今天分享一个python的小模块: linecache, 可以用它方便地获取某一文件某 ...

  2. 读书笔记jvm探秘之一:内存概况

    jvm内存大致可以分为六大块: 堆,虚拟机主要内存,可以形象的说,堆是对象的存储库,几乎所有的对象实例和数组都在此分配内存,当然也死于此,jvm垃圾回收机制(简称GC)主要处理的就是这个地方.它被所有 ...

  3. Java开发配置

    http://www.runoob.com/java/java-environment-setup.html

  4. laravel5.2总结--数据库操作

    1 配置信息 1.1配置目录: config/database.php 1.2配置多个数据库 //默认的数据库 'mysql' => [ 'driver' => 'mysql', 'hos ...

  5. 可实现一键分享到多个平台(微信,微博,qq空间,人人等)

    友推是一款是面向移动应用的SDK分享组件,提供给开发者集成使用.通过友推,开发者可以轻松集成社会化分享功能,同时创建及管理推荐好友使用您应用的推荐奖励活动,用户推荐好友安装使用您的应用即可获得推荐奖励 ...

  6. bash shell命令与监测的那点事(一)

    bash shell命令与监测的那点事之ps 学习LInux,不得不谈谈bash shell命令,介绍Linux命令行与Shell脚本的书有很多很多,bash shell命令也有很多,此次我们只谈谈有 ...

  7. Eclipse启动错误:A Java Runtime Environment(JRE) or Java Development Kit(JDK) must be available……

    确保Jdk,Jre都安装完成并且环境变量配置无误的情况下,自动Ecplise报错如下: A Java Runtime Environment (JRE) or Java Development Kit ...

  8. Kafka 1.0版本发布

    Kafka 1.0版本发布 1.0.0 2017年11月1日发布 源码下载: kafka-1.0.0-src.tgz(asc,sha512) 二进制下载: Scala 2.11 - kafka_2.1 ...

  9. 设计模式(一)单例模式:2-懒汉模式(Lazy)

    思想: 相比于饿汉模式,懒汉模式实际中的应用更多,因为在系统中,“被用到时再初始化”是更佳的解决方案. 设计思想与饿汉模式类似,同样是持有一个自身的引用,只是将 new 的动作延迟到 getinsta ...

  10. hihoCoder #1867 GCD

    在集合 $[n]$ 上使用容斥原理. 固定 $i$,考虑有多少个 $j \in [n]$ 满足 $\gcd(i, j) = \gcd(a_i, a_j) = 1$,将此数目记作 $f_i$.暂时不考虑 ...