Ubuntu 16.04上Linux C++程序性能分析工具perf使用入门

目前,perfLinux系统上最全面最方便的一个性能检测工具。由Linux内核携带并且同步更新。

Ubuntu 16.04系统上需要执行如下命令安装:

# 常规内核使用
#sudo apt-get install linux-tools-generic
# 低延时内核使用
#sudo apt-get install linux-tools-lowlatency

# 因此我们根据内核自动选择安装
$ sudo apt-get install linux-tools-`uname -r | cut -d- -f1-2`-`uname -r | cut -d- -f3`

$ sudo apt-get install linux-tools-common

#解决报错"Kernel address maps (/proc/{kallsyms,modules}) were restricted. Check /proc/sys/kernel/kptr_restrict before running 'perf record'".

$ sudo sh -c " echo 0 > /proc/sys/kernel/kptr_restrict"

使用方法如下(gcc编译时最好使用-g参数,生成符号,方便调试):

#生成性能日志文件,默认生成 perf.data
$ sudo perf record -e cpu-clock -g ./hello

#解析性能日志
$ perf report -g -i perf.data

参考链接


Linux C++程序进行性能分析工具gprof使用入门

软件的性能是软件质量的重要考察点,不论是在线服务程序还是离线程序,甚至是终端应用,性能都是用户体验的关键。这里说的性能重大的范畴来讲包括了性能和稳定性两个方面,我们在做软件测试的时候也是要重点测试版本的性能表现和稳定性的。对于软件测试过程中发现的性能问题,如何定位有很多的方法。基本的方法可能是开发者对代码进行review,或者是使用一些工具对代码进行性能分析。常见的性能分析工具有哪些呢?以下两篇文章做了详细的总结:

gprof是可用于Linux C++代码性能分析工具之一。
继续阅读Linux C++程序进行性能分析工具gprof使用入门

error: call to ‘__open_missing_mode’ declared with attribute error: open with O_CREAT or O_TMPFILE in second argument needs 3 arguments

使用open函数的时候,如果在第二个参数中使用了O_CREAT,就必须添加第三个参数:创建文件时赋予的初始权。

In function ‘open’,
    inlined from ‘test_detector_ffmpeg’ at ./src/ff_darknet.c:421:15:
/usr/include/x86_64-linux-gnu/bits/fcntl2.h:50:4: error: call to ‘__open_missing_mode’ declared with attribute error: open with O_CREAT or O_TMPFILE in second argument needs 3 arguments
    __open_missing_mode ();
    ^
compilation terminated due to -Wfatal-errors.

建议使用如下参数创建文件:

//0755
fd = open(file_path,O_CREAT | O_APPEND | O_WRONLY,S_IRWXU|S_IRUSR|S_IXUSR|S_IROTH|S_IXOTH);

macOS Sierra (10.12.3)使用Eclipse IDE for C/C++ Developers结合CMake Editor编辑Linux MakeFile项目

使用macOS Sierra(10.12.3)开发C/C++项目,经常用到网上的开源项目,很多项目是直接用MakeFile来管理项目的,导致在调试,编辑项目的时候,比较麻烦,搜索了半天,才找到目前看来比较方便的方式,就是结合Eclipse IDE for C/C++ DevelopersCMake Editor的方式来进行处理。

继续阅读macOS Sierra (10.12.3)使用Eclipse IDE for C/C++ Developers结合CMake Editor编辑Linux MakeFile项目

在VC中用CMarkup类操纵XML

首先到http://www.firstobject.com/dn_markup.htm上面下载CMarkup类,将CMarkup.cppCMarkup.h导入到我们的工程中就可以了。编译可能会出现问题,解决的方法是在CMarkup.cpp的开头加上#include,或者关闭预编译也可以。

1.创建一个XML文档

对于创建一个XML文档,需要实例化一个CMarkup对象,并调用AddElem创建根元素。在这个位置,如果你调用AddElem("School"),文档会简单的装一个空元素. 然后调用AddChildElem在根元素的下面创建元素。

CMarkup xml;
xml.SetDoc("\r\n");
xml.AddElem("School");
xml.IntoElem();
xml.AddElem("Department","Automation");
xml.AddElem("Department","Computer");
xml.AddElem("Department","Math");
xml.AddElem("Department","English");
xml.OutOfElem();
xml.Save("School.xml");

效果如下:

<?xml version="1.0" encoding="UTF-8"?>
<School>
    <Department>Automation</Department>
    <Department>Computer</Department>
    <Department>Math</Department>
    <Department>English</Department>
</School>

2.浏览特定元素

CMarkup xml;
xml.Load("School.xml");
BOOL bFind=TRUE;
xml.ResetMainPos();

while(xml.FindChildElem("Department"))
{
	CString strTagName=_T("");
	CString strData=_T("");
	strTagName=xml.GetChildTagName();    //此时节点是父节点
	strData=xml.GetChildData();
	TRACE("\n---tagName:%s,Data:%s--\n",strTagName,strData);
}

//***********************这样也可以*****************************/

CMarkup xml;
xml.Load("School.xml");
BOOL bFind = TRUE;
xml.ResetMainPos();

while (xml.FindChildElem("Department"))        
{
	xml.IntoElem();
	CString strTagName = _T("");
	CString strData = _T("");
	strTagName = xml.GetTagName();
	strData = xml.GetData();
	TRACE("\n---tagName:%s,Data:%s--\n",strTagName,strData);
	xml.OutOfElem();
}

//*********************或者这样也可以*************************/

CMarkup xml;
xml.Load("School.xml");
BOOL bFind = TRUE;
xml.ResetMainPos();
xml.FindElem(); //School
xml.IntoElem();

while (xml.FindElem("Department"))
{
	CString strTagName = _T("");
	CString strData = _T("");
	strTagName = xml.GetTagName();
	strData = xml.GetData();
	TRACE("\n---tagName:%s,Data:%s--\n",strTagName,strData);
}

//***********结果**************

---tagName:Department,Data:Automation--

---tagName:Department,Data:Computer--

---tagName:Department,Data:Math--

---tagName:Department,Data:English--

3.修改元素
//把Department为"English"改为"Chinese"

CMarkup xml;
BOOL bLoadXml=FALSE;
bLoadXml=xml.Load("School.xml");

if(bLoadXml)
{
	CString str=_T("");
	xml.ResetMainPos();
	xml.FindElem();
	xml.IntoElem();
	while(xml.FindElem("Department"))
	{
		str=xml.GetData();
		if(str=="English")
		{
			xml.SetData("Chinese");
			xml.Save("School.xml");
			break;
		}
	}
}

效果如下:

<?xml version="1.0" encoding="UTF-8"?>
<School>
	<Department>Automation</Department>
	<Department>Computer</Department>
	<Department>Math</Department>
	<Department>Chinese</Department>
</School>

4.添加
4.1 添加在最后面(用AddElem

CMarkup xml;
BOOL bLoadXml=FALSE;
bLoadXml=xml.Load("School.xml");
if(bLoadXml)
{
	xml.ResetMainPos();
	xml.FindElem();
	xml.IntoElem();
	xml.AddElem("Department","SiQinghua");
	xml.OutOfElem();
	xml.Save("School.xml");
}

效果如下:

<?xml version="1.0" encoding="UTF-8"?>
<School>
    <Department>Automation</Department>
    <Department>Computer</Department>
    <Department>Math</Department>
    <Department>Chinese</Department>
    <Department>SiQinghua</Department>
</School>

4.2 添加在最前面(用InsertElem

CMarkup xml;
BOOL bLoadXml=FALSE;
bLoadXml=xml.Load("School.xml");
if(bLoadXml)
{
	xml.ResetMainPos();
	xml.FindElem();
	xml.IntoElem();
	xml.AddElem("Department","SiQinghua");
	xml.OutOfElem();
	xml.Save("School.xml");
}

效果如下:

<?xml version="1.0" encoding="UTF-8"?>
<School>
	<Department>NARI</Department>
	<Department>Automation</Department>
	<Department>Computer</Department>
	<Department>Math</Department>
	<Department>Chinese</Department>
	<Department>SiQinghua</Department>
</School>

5.删除

CMarkup xml;
BOOL bLoadXml=FALSE;
bLoadXml=xml.Load("School.xml");
if(bLoadXml)
{
	BOOL bFind=TRUE;
	xml.ResetMainPos();
	while(bFind)
	{
		bFind=xml.FindChildElem("Department");
		if(bFind)
		{
			CString strData=_T("");
			strData=xml.GetChildData();    //此时节点还是父节点
			if("SiQinghua"==strData)
			{
				xml.RemoveChildElem();
				xml.Save("School.xml");
				break;
			}
		}
	}
}

效果如下:

<?xml version="1.0" encoding="UTF-8"?>
<School>
	<Department>NARI</Department>
	<Department>Automation</Department>
	<Department>Computer</Department>
	<Department>Math</Department>
	<Department>Chinese</Department>
</School>

上面的例子都是不带属性的,下面举一个带有属性的例子。
1.生成XML文档

CMarkup xml;
xml.SetDoc("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
xml.AddElem("UserInfo");
xml.IntoElem();
for(int i=0;i<10;i++)
{
	CString strID=_T("");
	CString strPwd=_T("");

	srand(UINT(time(NULL)+i)); //产生一个随机数
	int nLevel=rand()%256;

	strID.Format("UserID%02d",i);
	strPwd.Format("UserPwd%02d",i);

	xml.AddElem("User");
	xml.AddAttrib("UserID",strID);
	xml.AddAttrib("UserPwd",strPwd);
	xml.AddAttrib("UserLevel",nLevel);
}
xml.OutOfElem();
xml.Save("UserInfo.xml");

效果如下:

<?xml version="1.0" encoding="UTF-8"?>
<UserInfo>
	<User UserID="UserID00" UserPwd="UserPwd00" UserLevel="85"/>
	<User UserID="UserID01" UserPwd="UserPwd01" UserLevel="88"/>
	<User UserID="UserID02" UserPwd="UserPwd02" UserLevel="92"/>
	<User UserID="UserID03" UserPwd="UserPwd03" UserLevel="95"/>
	<User UserID="UserID04" UserPwd="UserPwd04" UserLevel="98"/>
	<User UserID="UserID05" UserPwd="UserPwd05" UserLevel="101"/>
	<User UserID="UserID06" UserPwd="UserPwd06" UserLevel="105"/>
	<User UserID="UserID07" UserPwd="UserPwd07" UserLevel="108"/>
	<User UserID="UserID08" UserPwd="UserPwd08" UserLevel="111"/>
	<User UserID="UserID09" UserPwd="UserPwd09" UserLevel="115"/>
</UserInfo>

2.查找特定属性

CMarkup xml;
BOOL bLoadXml=FALSE;
BOOL bFind=FALSE;
bLoadXml=xml.Load("UserInfo.xml");
if(bLoadXml)
{
	CString strID;
	CString strPwd;
	xml.ResetMainPos();
	xml.FindElem();    //UserInfo
	while(xml.FindChildElem("User"))
	{
		strID=xml.GetChildAttrib("UserID");
		strPwd=xml.GetChildAttrib("UserPwd");
		TRACE("\n---id:%s,pwd:%s---\n",strID,strPwd);
		if(strID=="UserID02" && strPwd=="UserPwd02")
		{
			bFind=TRUE;
			break;
		}
	}
}
if(bFind==TRUE)
	TRACE("\n---find---\n");
else
	TRACE("\n---not find---\n");

3.修改特定属性

CMarkup xml;
BOOL bFind=FALSE;
BOOL bLoadXml=FALSE;
bLoadXml=xml.Load("UserInfo.xml");
if(bLoadXml)
{
	CString strID;
	CString strPwd;
	xml.ResetMainPos();
	xml.FindElem();    //UserInfo
	while(xml.FindChildElem("User"))
	{
		strID=xml.GetChildAttrib("UserID");
		strPwd=xml.GetChildAttrib("UserPwd");
		TRACE("\n--id:%s,pwd:%s--\n",strID,strPwd);
		if(strID=="UserID02" && strPwd=="UserPwd02")
		{
			bFind=TRUE;
			xml.SetChildAttrib("UserID",strID+CString("Modified"));
			xml.Save("UserInfo.xml");
			break;
		}
	}
}
if(bFind==TRUE)
	TRACE("\n---find---\n");
else
	TRACE("\n---not find---\n");

效果如下:

<?xml version="1.0" encoding="UTF-8"?>
<UserInfo>
	<User UserID="UserID00" UserPwd="UserPwd00" UserLevel="85"/>
	<User UserID="UserID01" UserPwd="UserPwd01" UserLevel="88"/>
	<User UserID="UserID02Modified" UserPwd="UserPwd02" UserLevel="92"/>
	<User UserID="UserID03" UserPwd="UserPwd03" UserLevel="95"/>
	<User UserID="UserID04" UserPwd="UserPwd04" UserLevel="98"/>
	<User UserID="UserID05" UserPwd="UserPwd05" UserLevel="101"/>
	<User UserID="UserID06" UserPwd="UserPwd06" UserLevel="105"/>
	<User UserID="UserID07" UserPwd="UserPwd07" UserLevel="108"/>
	<User UserID="UserID08" UserPwd="UserPwd08" UserLevel="111"/>
	<User UserID="UserID09" UserPwd="UserPwd09" UserLevel="115"/>
</UserInfo>

参考链接


在VC中用CMarkup类操纵XML

C/C++中的正则表达式库——PCRE, PCRE++

1. 什么是PCRE? 什么是PCRE++?
PCRE,全称是Perl Compatible Regular Expressions。从名字我们可以看出PCRE库是与Perl中正则表达式相兼容的一个正则表达式库。PCRE是免费开源的库,它是由C语言实现的,这里是它的官方主页:http://www.pcre.org/,感兴趣的朋友可以在这里了解更多的内容。
要得到PCRE库,可以从这里下载:http://sourceforge.net/projects/pcre/files/

PCRE++是一个对PCRE库的C++封装,它提供了更加方便、易用的C++接口。这里是它的官方主页:http://www.daemon.de/PCRE,感兴趣的朋友可以在这里了解更多的内容。
要得到PCRE++库,可以从这里下载:http://www.daemon.de/PcreDownload

2. PCRE接口介绍
(1). pcre_compile

pcre *pcre_compile(const char *pattern, int options,
            const char **errptr, int *erroffset,
            const unsigned char *tableptr);
功能:编译指定的正则表达式
参数:pattern, 输入参数,将要被编译的字符串形式的正则表达式
      options, 输入参数,用来指定编译时的一些选项
      errptr, 输出参数,用来输出错误信息
      erroffset, 输出参数,pattern中出错位置的偏移量
      tableptr, 输入参数,用来指定字符表,一般情况用NULL, 使用缺省的字符表
返回值:被编译好的正则表达式的pcre内部表示结构

(2). pcre_exec

int pcre_exec(const pcre *code, const pcre_extra *extra,
            const char *subject, int length, int startoffset,
            int options, int *ovector, int ovecsize);
功能:用来检查某个字符串是否与指定的正则表达式匹配
参数: code, 输入参数,用pcre_compile编译好的正则表达结构的指针
      extra, 输入参数,用来向pcre_exec传一些额外的数据信息的结构的指针
      subject, 输入参数,要被用来匹配的字符串
      length, 输入参数, 要被用来匹配的字符串的指针
      startoffset, 输入参数,用来指定subject从什么位置开始被匹配的偏移量
      options, 输入参数, 用来指定匹配过程中的一些选项
      ovector, 输出参数,用来返回匹配位置偏移量的数组
      ovecsize, 输入参数, 用来返回匹配位置偏移量的数组的最大大小
返回值:匹配成功返回非负数,匹配返回负数

3. PCRE++接口介绍
PCRE++PCRE库封装成了两个类,一个是RE_Options, 用来指定匹配选项,一个是RE,用来提供匹配相关的接口。RE_options类在这里我就不介绍了,我主要介绍一下RE类:
(1)RE的构造函数传入正则表达式,并在构造函数中调用Init函数,将该正则表达进行编译
(2)REpattern()成员用来得到初始传入的正则表达式字符串
(3)REerror()成员用来得到匹配过程中的出错信息
(4)REFullMatch()成员用来判断某字符串整体是否匹配指定正则表达式
(5)REPartialMatch()成员用来判断某字符串的部分是否匹配指定正则表达式

4. PCRE/PCRE++使用注意事项
(1)使用pcre请包含pcre.h头文件
(2)使用pcre_compile, pcre_exec后,记得调用pcre_free释放内存,以免造成内存泄露
(3)使用pcre编译的时候需要依赖libpcre.a
(4)使用pcre++请包含pcrecpp.h头文件
(5)使用pcre++RE类的析构函数会自动释放相关内存,因此不用担心内存泄露
(6)使用pcre++编译的时候需要依赖libpcrecpp.a
(7)使用pcrecpp要使用pcrecpp命名空间

5. PCRE使用举例
下面是例程:

#include <pcre.h>
#include <stdio.h>
#include <string.h>
 
int main(int argc, char ** argv)
{
    if (argc != 3)
    {   
        printf("Usage: %s pattern text\n", argv[0]);
        return 1;
    }   
 
    const char * pPattern = argv[1];
    const char * pText = argv[2];
    const char * pErrMsg = NULL;
    pcre * pPcre = NULL;
    int nOffset = -1; 
 
    if (NULL == (pPcre = pcre_compile(pPattern, 0, &pErrMsg, &nOffset, NULL)))
    {   
        printf("ErrMsg=%s, Offset=%d\n", pErrMsg, nOffset);
        return 1;
    }   
    else
    {   
        if (pcre_exec(pPcre, NULL, pText, strlen(pText), 0, 0, NULL, 0) < 0)
        {   
            printf("%s doesn't match %s\n", pText, pPattern);
        }   
        else
        {   
            printf("%s matches %s\n", pText, pPattern);
        }
    }
}

下面是运行结果:

$ g++ -lpcre TestPcre.cpp -o pcre

$ ./pcre "http:\/\/.*\.qq\.com" "http://www.qq.com"
http://www.qq.com matches http:\/\/.*\.qq\.com
$ ./pcre "http:\/\/.*\.qq\.com" "http://www.qqq.com"
http://www.qqq.com doesn't match http:\/\/.*\.qq\.com

6. PCRE++使用举例
下面是例程:

#include <iostream>
#include <pcrecpp.h>
 
int main(int argc, char ** argv)
{
    if (argc != 3)
    {   
        std::cerr < < "Usage: " << argv[0] << " pattern text\n";
        return 1;
    }   
 
    pcrecpp::RE oPattern(argv[1]);
    if (oPattern.FullMatch(argv[2]))
    {   
        std::cout << argv[2] << " fully matches " << argv[1] << "\n";
    }   
    else if (oPattern.PartialMatch(argv[2]))
    {   
        std::cout << argv[2] << " partially matches " << argv[1] << "\n";
    }   
    else
    {   
        std::cout << argv[2] << " dose not match " << argv[1] << "\n";
    }   
}

下面是运行结果:

$ g++ TestPcreCpp.cpp -lpcrecpp -o pcrecpp

$ ./pcrecpp 
Usage: ./pcrecpp pattern text
$ ./pcrecpp "http:\/\/.*\.qq\.com" "http://www.qq.com"
http://www.qq.com fully matches http:\/\/.*\.qq\.com
$ ./pcrecpp "http:\/\/.*\.qq\.com" "http://www.qq.comiii"
http://www.qq.comiii partially matches http:\/\/.*\.qq\.com
$ ./pcrecpp "http:\/\/.*\.qq\.com" "http://www.qqq.comiii"
http://www.qqq.comiii dose not match http:\/\/.*\.qq\.com

参考链接


深入浅出C/C++中的正则表达式库(三)——PCRE, PCRE++

fatal error LNK1112: 模块计算机类型“X86”与目标计算机类型“x64”冲突-解决

编译项目,遇到了这个问题

查了很多地方,解决方案是:

1.右键项目,属性,最顶端,配置为x64或者x86(如果没有该选项就新建)

2.链接器-高级,目标计算机选为一致的

运行还是会报这个错误

最后发现

3.链接器-命令行

最底部有一条指令,修改为一致的机型就可以了

android-ndk-r10e开启C++11,编译TEMP_FAILURE_RETRY错误

在使用select来操作socket的时候,一般都是会这么写

int err = TEMP_FAILURE_RETRY(select(socket_fd + 1, NULL, &set, NULL, const_cast<struct timeval*>(&timeout)));

其中的“TEMP_FAILURE_RETRY”宏在“unistd.h”中的定义如下:

/* Used to retry syscalls that can return EINTR. */
#define TEMP_FAILURE_RETRY(exp) ({         \
    typeof (exp) _rc;                      \
    do {                                   \
        _rc = (exp);                       \
    } while (_rc == -1 && errno == EINTR); \
    _rc; })

正常情况下,编译是没问题的。但是当在“Application.mk”中增加

APP_CPPFLAGS += -std=c++11

之后,发现编译不通过了。报告“error: 'typeof' was not declared in this scope”。

解决方法:

APP_CPPFLAGS += -std=gun++11

“c++11”和“gnu++11”的差别:

“gnu++11”增加了很多的扩展“c++11”的功能,功能更加多,具体的扩展参考Extensions to the C++ Language

NDK下GCC定义__cplusplus不正确的问题

在C++升级之后,编译会出现“C++11 error: unable to find string literal operator 'operator"”这种错误,按照链接里面操作,在“android-ndk-r10e”上面依旧出现问题。

追踪了一下,才发现,这个是GCC的一个BUG,GCC-4.7,GCC-4.8中定义的__cplusplus 竟然都是 “__cplusplus=1”,这个是明显不正确的,具体的BUG内容查看“__cplusplus defined to 1, should be 199711L”。

尽管这个BUG已经修复了,但是很明显“android-ndk-r10e”使用的GCC版本并没有合并这个补丁。

解决方案

升级GCC到4.9,在”Application.mk“中增加

NDK_TOOLCHAIN_VERSION = 4.9

SharedWorker源码解析

最近工作需要了解WebWorker,根据RTFSC原则,空下来看一下Chrome的SharedWorker源码。

ShareWorker是共用Worker,Chrome的实现调用new SharedWorker会分配独立进程,不管调用多少次都只有这一个实例。除了进程管理,SharedWorker还需要通信,Chrome中SharedWorker通过MessagePort通信。

SharedWorker.cpp

// 构造函数,
inline SharedWorker::SharedWorker(ExecutionContext* context)
// 初始化父类AbstrctWorker,
// AbstractWorker继承自ActiveDOMObject,因为要在JS中使用,所以Worker需要是一个DOM Object。暂时不关注ActiveDOMObject的实现。
// AbstractWorker有一个方法,resolveURL,对传进来的url进行有效性和安全性检查。
: AbstractWorker(context)
    //初始化连接标志m_isBeingConnected。
      , m_isBeingConnected(false)
  {
  }

//接下来是SharedWorker真正创建的函数,create:
  PassRefPtrWillBeRawPtr<SharedWorker> SharedWorker::create(ExecutionContext* context, const String& url, const String& name, ExceptionState& exceptionState)
  {
    // SharedWorker是独立进程的
    ASSERT(isMainThread());
    // 因为目前还不支持worker与worker之间通信,context必须是JS中的document
    ASSERT_WITH_SECURITY_IMPLICATION(context->isDocument());

    // 引用计数,SharedWorker会被多次引用,所以需要引用计数,以便最后一个引用退出时析构。
    UseCounter::count(context, UseCounter::SharedWorkerStart);

    // 使用构造函数构造一个worker实例。
    RefPtrWillBeRawPtr<SharedWorker> worker = adoptRefWillBeNoop(new SharedWorker(context));// SharedWorker的构造函数并没有做太多的事情,初始化父类AbstractWorker,以及一个标志,是否连接

    // 通信管道,MessageChannel放到后面看。
    MessageChannel* channel = MessageChannel::create(context);
    // port是从channel中拿到的,管道中用来通信的
    worker->m_port = channel->port1();
    OwnPtr<WebMessagePortChannel> remotePort = channel->port2()->disentangle();
    ASSERT(remotePort);

    // 这个看名字就知道什么意思了
    worker->suspendIfNeeded();

    // 这里chrome已经注释了
    // We don't currently support nested workers, so workers can only be created from documents.
    Document* document = toDocument(context);
    // 判断下能不能连上
    if (!document->securityOrigin()->canAccessSharedWorkers()) {
      exceptionState.throwSecurityError("Access to shared workers is denied to origin '" + document->securityOrigin()->toString() + "'.");
      return nullptr;
    }

    // 判断url的安全性
    KURL scriptURL = worker->resolveURL(url, exceptionState);
    if (scriptURL.isEmpty())
      return nullptr;

    // 创建document与worker的连接
    if (document->frame()->loader().client()->sharedWorkerRepositoryClient())
      document->frame()->loader().client()->sharedWorkerRepositoryClient()->connect(worker.get(), remotePort.release(), scriptURL, name, exceptionState);

    // 返回worker的实例
    return worker.release();
  }

  SharedWorker::~SharedWorker()
  {
  }

  const AtomicString& SharedWorker::interfaceName() const
      {
          return EventTargetNames::SharedWorker;
}

bool SharedWorker::hasPendingActivity() const
    {
        return m_isBeingConnected;
}

其实SharedWorker挺简单的,进程管理,通信,下面看通信是如何实现的(急需恶补一番底层通信知识,预定下周把底层通信手段学习一遍)。

static void createChannel(MessagePort* port1, MessagePort* port2)
{
  // 创建连接
  WebMessagePortChannel* channel1;
  WebMessagePortChannel* channel2;
  Platform::current()->createMessageChannel(&channel1, &channel2);
  ASSERT(channel1 && channel2);

  // Now entangle the proxies with the appropriate local ports.
  port1->entangle(adoptPtr(channel2));
  port2->entangle(adoptPtr(channel1));
}

MessageChannel::MessageChannel(ExecutionContext* context)
// MessageChannel里面就是搞两个port
: m_port1(MessagePort::create(*context))
, m_port2(MessagePort::create(*context))
{
  createChannel(m_port1.get(), m_port2.get());
}

好吧,这个类也不怎么干活啊,活在WebMessagePortChannel和MessagePort里面做。

MessagePort

// 如果这里是管道通信的话,那可能需要两组channel worker端一组,web端一组
// 这是一个干活的类,create里面做的事情不多,构造一下,返回
PassRefPtrWillBeRawPtr<MessagePort> MessagePort::create(ExecutionContext& executionContext)
{
  RefPtrWillBeRawPtr<MessagePort> port = adoptRefWillBeNoop(new MessagePort(executionContext));
  port->suspendIfNeeded();
  return port.release();
}

//构造函数做的也不多,创建一个跟document关联的DOM Object,初始化几个标志参数
MessagePort::MessagePort(ExecutionContext& executionContext)
: ActiveDOMObject(&executionContext)
, m_started(false)
    , m_closed(false)
    , m_weakFactory(this)
{
}

MessagePort::~MessagePort()
{
  close();
  if (m_scriptStateForConversion)
    m_scriptStateForConversion->disposePerContextData();
}

// 发消息
void MessagePort::postMessage(ExecutionContext* context, PassRefPtr<SerializedScriptValue> message, const MessagePortArray* ports, ExceptionState& exceptionState)
{
  // entangle,好吧,英语比较渣,google翻译下,是缠的意思,Orz
  if (!isEntangled())
    return;
  ASSERT(executionContext());
  ASSERT(m_entangledChannel);

  // 一个channel数组
  OwnPtr<MessagePortChannelArray> channels;
  // Make sure we aren't connected to any of the passed-in ports.
  // 防错代码
  if (ports) {
    for (unsigned i = 0; i < ports->size(); ++i) {
      MessagePort* dataPort = (*ports)[i].get();
      if (dataPort == this) {
        exceptionState.throwDOMException(DataCloneError, "Port at index " + String::number(i) + " contains the source port.");
        return;
      }
    }
    // 解绑
    channels = MessagePort::disentanglePorts(context, ports, exceptionState);
    if (exceptionState.hadException())
      return;
  }

  // 给channel发消息
  WebString messageString = message->toWireString();
  OwnPtr<WebMessagePortChannelArray> webChannels = toWebMessagePortChannelArray(channels.release());
  // 待看 WebMessagePortChannelArray
  m_entangledChannel->postMessage(messageString, webChannels.leakPtr());
}

// static 两组管道互相取
PassOwnPtr<WebMessagePortChannelArray> MessagePort::toWebMessagePortChannelArray(PassOwnPtr<MessagePortChannelArray> channels)
{
  OwnPtr<WebMessagePortChannelArray> webChannels;
  if (channels && channels->size()) {
    webChannels = adoptPtr(new WebMessagePortChannelArray(channels->size()));
    for (size_t i = 0; i < channels->size(); ++i)
    (*webChannels)[i] = (*channels)[i].leakPtr();
  }
  return webChannels.release();
}

// static
PassOwnPtrWillBeRawPtr<MessagePortArray> MessagePort::toMessagePortArray(ExecutionContext* context, const WebMessagePortChannelArray& webChannels)
{
  OwnPtrWillBeRawPtr<MessagePortArray> ports = nullptr;
  if (!webChannels.isEmpty()) {
    OwnPtr<MessagePortChannelArray> channels = adoptPtr(new MessagePortChannelArray(webChannels.size()));
    for (size_t i = 0; i < webChannels.size(); ++i)
    (*channels)[i] = adoptPtr(webChannels[i]);
    ports = MessagePort::entanglePorts(*context, channels.release());
  }
  return ports.release();
}

// 函数字面意思是,断开缠绕关系
PassOwnPtr<WebMessagePortChannel> MessagePort::disentangle()
{
  ASSERT(m_entangledChannel);
  // 将端口重置为0
  m_entangledChannel->setClient(0);
  return m_entangledChannel.release();
}

// Invoked to notify us that there are messages available for this port.
// This code may be called from another thread, and so should not call any non-threadsafe APIs (i.e. should not call into the entangled channel or access mutable variables).
// 通知port已经可用了,即start过了
void MessagePort::messageAvailable()
{
  ASSERT(executionContext());
  executionContext()->postTask(FROM_HERE, createCrossThreadTask(&MessagePort::dispatchMessages, m_weakFactory.createWeakPtr()));
}

// 类似于生命周期,start了通知一声
void MessagePort::start()
{
  // Do nothing if we've been cloned or closed.
  if (!isEntangled())
    return;

  ASSERT(executionContext());
  if (m_started)
    return;

  m_started = true;
  messageAvailable();
}

// 关掉缠绕关系
void MessagePort::close()
{
  if (isEntangled())
    m_entangledChannel->setClient(0);
  m_closed = true;
}

// 与远程channel缠绕
void MessagePort::entangle(PassOwnPtr<WebMessagePortChannel> remote)
{
  // Only invoked to set our initial entanglement.
  ASSERT(!m_entangledChannel);
  ASSERT(executionContext());

  m_entangledChannel = remote;
  m_entangledChannel->setClient(this);
}

const AtomicString& MessagePort::interfaceName() const
    {
        return EventTargetNames::MessagePort;
}

// 尝试从webChannel取message
static bool tryGetMessageFrom(WebMessagePortChannel& webChannel, RefPtr<SerializedScriptValue>& message, OwnPtr<MessagePortChannelArray>& channels)
{
  WebString messageString;
  WebMessagePortChannelArray webChannels;
  if (!webChannel.tryGetMessage(&messageString, webChannels))
  return false;

  if (webChannels.size()) {
    channels = adoptPtr(new MessagePortChannelArray(webChannels.size()));
    for (size_t i = 0; i < webChannels.size(); ++i)
    (*channels)[i] = adoptPtr(webChannels[i]);
  }
  message = SerializedScriptValueFactory::instance().createFromWire(messageString);
  return true;
}

bool MessagePort::tryGetMessage(RefPtr<SerializedScriptValue>& message, OwnPtr<MessagePortChannelArray>& channels)
{
  if (!m_entangledChannel)
    return false;
  return tryGetMessageFrom(*m_entangledChannel, message, channels);
}

// 分发消息
void MessagePort::dispatchMessages()
{
  // Because close() doesn't cancel any in flight calls to dispatchMessages() we need to check if the port is still open before dispatch.
  if (m_closed)
    return;

  // Messages for contexts that are not fully active get dispatched too, but JSAbstractEventListener::handleEvent() doesn't call handlers for these.
  // The HTML5 spec specifies that any messages sent to a document that is not fully active should be dropped, so this behavior is OK.
  if (!started())
    return;

  RefPtr<SerializedScriptValue> message;
  OwnPtr<MessagePortChannelArray> channels;
  // 从管道中拿到message
  while (tryGetMessage(message, channels)) {
    // close() in Worker onmessage handler should prevent next message from dispatching.
    if (executionContext()->isWorkerGlobalScope() && toWorkerGlobalScope(executionContext())->isClosing())
      return;

    // 这里有一次绑定端口
    OwnPtrWillBeRawPtr<MessagePortArray> ports = MessagePort::entanglePorts(*executionContext(), channels.release());
    RefPtrWillBeRawPtr<Event> evt = MessageEvent::create(ports.release(), message.release());

    dispatchEvent(evt.release(), ASSERT_NO_EXCEPTION);
  }
}

bool MessagePort::hasPendingActivity() const
    {
      // The spec says that entangled message ports should always be treated as if they have a strong reference.
      // We'll also stipulate that the queue needs to be open (if the app drops its reference to the port before start()-ing it, then it's not really entangled as it's unreachable).
        return m_started && isEntangled();
}

// 解除port entangle关系?
PassOwnPtr<MessagePortChannelArray> MessagePort::disentanglePorts(ExecutionContext* context, const MessagePortArray* ports, ExceptionState& exceptionState)
{
  if (!ports || !ports->size())
    return nullptr;

  // HashSet used to efficiently check for duplicates in the passed-in array.
  HashSet<MessagePort*> portSet;

  // Walk the incoming array - if there are any duplicate ports, or null ports or cloned ports, throw an error (per section 8.3.3 of the HTML5 spec).
  for (unsigned i = 0; i < ports->size(); ++i) {
  MessagePort* port = (*ports)[i].get();
  if (!port || port->isNeutered() || portSet.contains(port)) {
    String type;
    if (!port)
      type = "null";
    else if (port->isNeutered())
      type = "already neutered";
    else
      type = "a duplicate";
    exceptionState.throwDOMException(DataCloneError, "Port at index "  + String::number(i) + " is " + type + ".");
    return nullptr;
  }
  portSet.add(port);
}

  UseCounter::count(context, UseCounter::MessagePortsTransferred);

  // Passed-in ports passed validity checks, so we can disentangle them.
  // 每个port disentangle
  OwnPtr<MessagePortChannelArray> portArray = adoptPtr(new MessagePortChannelArray(ports->size()));
  for (unsigned i = 0; i < ports->size(); ++i)
  (*portArray)[i] = (*ports)[i]->disentangle();
  return portArray.release();
}

// 绑定所有ports
PassOwnPtrWillBeRawPtr<MessagePortArray> MessagePort::entanglePorts(ExecutionContext& context, PassOwnPtr<MessagePortChannelArray> channels)
{
  // https://html.spec.whatwg.org/multipage/comms.html#message-ports
  // |ports| should be an empty array, not null even when there is no ports.
  if (!channels || !channels->size())
    return adoptPtrWillBeNoop(new MessagePortArray());

  OwnPtrWillBeRawPtr<MessagePortArray> portArray = adoptPtrWillBeNoop(new MessagePortArray(channels->size()));
  for (unsigned i = 0; i < channels->size(); ++i) {
  RefPtrWillBeRawPtr<MessagePort> port = MessagePort::create(context);
  port->entangle((*channels)[i].release());
  (*portArray)[i] = port.release();
}
  return portArray.release();
}

主要就是entangle函数里面,会做一次remote.setClient(this),然后dispatchEvent就很方便了。疑问就是 为什么postMessage会做一次disentangle

就是port看的晕晕的,还是去理解一下底层通信吧.