Skip to content

Conversation

@solstice23
Copy link
Collaborator

@solstice23 solstice23 commented Mar 28, 2023

…, getLibraries)

readDirWithDetails: 获取目录下所有子文件/文件夹详细信息
getProperties: 获取文件属性
getDisks: 获取所有盘符
getLibraries: 获取 Windows 系统库路径

同时增加了 long, long long, std::map, std::variant 的 cef_v8value_t 转换

@std-microblock std-microblock changed the title Add native fs apis (readDirWithDetails, getProperties, getDisks… feat(api): Add native fs apis Mar 30, 2023
@std-microblock std-microblock merged commit 4babec0 into v2 Mar 30, 2023
@std-microblock std-microblock deleted the more-native-fs-apis branch March 30, 2023 15:03
@heroboy
Copy link

heroboy commented Apr 7, 2023

其实,我觉得这个不好。用得不好,界面就卡住了。
另外,我好奇new std::thread没有内存泄漏?跨线程调用js callback安全的吗?

@std-microblock
Copy link
Owner

其实,我觉得这个不好。用得不好,界面就卡住了。 另外,我好奇new std::thread没有内存泄漏?跨线程调用js callback安全的吗?

native api 是为小文件读取避免 async 和需要回调的场景设计的(比如 SMTC Buttons 回调),在数据量大的情况下确实可能会导致渲染线程卡住。但是也不推荐在数据量大时使用 native api,可能会导致 ipc 通道阻塞和大量内存泄漏(cef 论坛上有汇报 cpp 端发送的数据不会被 gc 自动回收的情况,不清楚 ncm 用的 cef 版本是否有这个问题),相反,应该使用 http server api(如截图那块的 api),不过延迟就会相对大一些

@std-microblock
Copy link
Owner

其实,我觉得这个不好。用得不好,界面就卡住了。 另外,我好奇new std::thread没有内存泄漏?跨线程调用js callback安全的吗?

new std::thread 在执行结束后最多会泄露 4 个字节,相比之下还是 native plugin 那边 string 参数漏的更多(

@std-microblock
Copy link
Owner

(问题最大的还是网易云本身刷新后完全不会回收上一个页面的内存,这个漏的太多我还很难修)

@std-microblock
Copy link
Owner

其实,我觉得这个不好。用得不好,界面就卡住了。 另外,我好奇new std::thread没有内存泄漏?跨线程调用js callback安全的吗?

JSFunction 那个类必须要用值捕获才行,或者自行加锁/atomic。毕竟是发送到v8线程去执行的

@heroboy
Copy link

heroboy commented Apr 7, 2023

没有注意到JSFunction这层封装。真复杂呀。实际运行是不是还是异步的post到v8线程的。
JSFunction也没有释放。是不是在JSFunction::operator()中等待exec执行完成,然后delete this比较好。
另外,new std::thread我觉得还泄露了一个线程内核对象HANDLE。

@std-microblock
Copy link
Owner

没有注意到JSFunction这层封装。真复杂呀。实际运行是不是还是异步的post到v8线程的。 JSFunction也没有释放。是不是在JSFunction::operator()中等待exec执行完成,然后delete this比较好。 另外,new std::thread我觉得还泄露了一个线程内核对象HANDLE。

应该不会,std::thread 在执行完成后会自动释放 HANDLE

@std-microblock
Copy link
Owner

没有注意到JSFunction这层封装。真复杂呀。实际运行是不是还是异步的post到v8线程的。 JSFunction也没有释放。是不是在JSFunction::operator()中等待exec执行完成,然后delete this比较好。 另外,new std::thread我觉得还泄露了一个线程内核对象HANDLE。

你说的这个方案我没考虑是因为 JSFunction 可能会被多次调用…如果直接 delete 就会导致多次调用失败,而且外部会悬空指针。就是没想到一个好的方案最后还是决定摆了(想到能用的可能是在最后一次执行写一个 flag,但是这样就很不优雅

实际确实异步 post 到 v8 线程的。

@heroboy
Copy link

heroboy commented Apr 7, 2023

没有注意到JSFunction这层封装。真复杂呀。实际运行是不是还是异步的post到v8线程的。 JSFunction也没有释放。是不是在JSFunction::operator()中等待exec执行完成,然后delete this比较好。 另外,new std::thread我觉得还泄露了一个线程内核对象HANDLE。

应该不会,std::thread 在执行完成后会自动释放 HANDLE

简单写了一段程序。测试了一下。不会。

int main(int argc, const char** argv)
{
	std::thread t = std::thread([]() {});
	DWORD ret = WaitForSingleObject((HANDLE)t.native_handle(), INFINITE);
	assert(ret == WAIT_OBJECT_0);            //线程执行完了
	HANDLE hh = t.native_handle();
	ret = WaitForSingleObject(hh, INFINITE);
	assert(ret == WAIT_OBJECT_0);            //等待成功表示hh是个有效的线程句柄
	//执行完之后,线程句柄并没有释放
	t.join();
	return 0;
}

@heroboy
Copy link

heroboy commented Apr 7, 2023

JSFunction 可能会被多次调用
这个就更离谱了。因为虽然我看到了有busy、valid等成员变量,想到你可能会有类似的想法。但是却完全没有用到任何线程同步的api,使得我认为这个类基本上线程不安全的。

另外,你的整个代码用到了各种c++高级的功能。但是在这一块却完全没用std::shared_ptr(虽然我也没一下子想到应该怎么正确使用来解决当下的问题)。但是使用那么多new,不符合这个项目的风格。

@std-microblock
Copy link
Owner

JSFunction 可能会被多次调用
这个就更离谱了。因为虽然我看到了有busy、valid等成员变量,想到你可能会有类似的想法。但是却完全没有用到任何线程同步的api,使得我认为这个类基本上线程不安全的。

另外,你的整个代码用到了各种c++高级的功能。但是在这一块却完全没用std::shared_ptr(虽然我也没一下子想到应该怎么正确使用来解决当下的问题)。但是使用那么多new,不符合这个项目的风格。

此处使用 shared_ptr 来管理内存会导致对象在 v8 线程中执行前, task已经被post出去后被清理掉,等到需要在 v8 线程中执行时对象就已经没了

@std-microblock
Copy link
Owner

没有注意到JSFunction这层封装。真复杂呀。实际运行是不是还是异步的post到v8线程的。 JSFunction也没有释放。是不是在JSFunction::operator()中等待exec执行完成,然后delete this比较好。 另外,new std::thread我觉得还泄露了一个线程内核对象HANDLE。

应该不会,std::thread 在执行完成后会自动释放 HANDLE

简单写了一段程序。测试了一下。不会。

int main(int argc, const char** argv)
{
	std::thread t = std::thread([]() {});
	DWORD ret = WaitForSingleObject((HANDLE)t.native_handle(), INFINITE);
	assert(ret == WAIT_OBJECT_0);            //线程执行完了
	HANDLE hh = t.native_handle();
	ret = WaitForSingleObject(hh, INFINITE);
	assert(ret == WAIT_OBJECT_0);            //等待成功表示hh是个有效的线程句柄
	//执行完之后,线程句柄并没有释放
	t.join();
	return 0;
}

记得在哪看到过一次,可能是我记错了,回头看源码查证一下

@std-microblock
Copy link
Owner

JSFunction 可能会被多次调用
这个就更离谱了。因为虽然我看到了有busy、valid等成员变量,想到你可能会有类似的想法。但是却完全没有用到任何线程同步的api,使得我认为这个类基本上线程不安全的。

busy和valid都是atomic var,可以保证在短时间内调用两次时会依次执行(好久以前写的了 应该是这样)

@std-microblock
Copy link
Owner

(另外可以把自己写的东西放在 quote 外,差点没看到这段)

@heroboy
Copy link

heroboy commented Apr 7, 2023

JSFunction 可能会被多次调用
这个就更离谱了。因为虽然我看到了有busy、valid等成员变量,想到你可能会有类似的想法。但是却完全没有用到任何线程同步的api,使得我认为这个类基本上线程不安全的。

busy和valid都是atomic var,可以保证在短时间内调用两次时会依次执行(好久以前写的了 应该是这样)

不是的。你看这一段:

const int operator()(Args... args) {
		if (!this->valid)return -1;
//如果两个线程同时开始执行operator()
//然后等待busy变成false
		while (this->busy); 
//然后可能这2个线程同时等到busy变成了false,然后同时开始执行下面的代码
		this->busy = true;
		auto task = static_cast<cef_task_post_exec*>(calloc(1, sizeof(cef_task_post_exec)));
		task->func = this;

所以应该用一个InterlockedCompareExchange这样的api,来原子的执行 if (busy == false) busy = true这样的语句。(std::atomic<bool>应该直接有这样的成员函数)

(引用的时候>符号那一行需要空一行才能脱离quote。有时候写的时候没注意看preview,就排版错了。)

@std-microblock
Copy link
Owner

噢噢 你是这个意思 可能确实会有这个问题 我现在去改

@std-microblock
Copy link
Owner

std::atomic 是有 CAS 操作的 api 的,这里没用确实瓜了)

std-microblock added a commit that referenced this pull request Apr 9, 2023
https: //github.com//pull/351#issuecomment-1500210856
Co-Authored-By: heroboy <54468+heroboy@users.noreply.github.com>
@heroboy
Copy link

heroboy commented Apr 10, 2023

我又思考了一下new std::thread的问题。
其实可以这么写:

std::thread([=](){

}).detach();

我觉得你使用new的原因是, std::thread在析构的时候会报错,这是因为std::thread析构前必须先joindetach,否则无论如何析构的时候都会报错的,即使线程已经运行结束了。

另外,我一开始没有说应该用detach的原因是,我当时以为std::thread析构的时候,会不会把捕获的变量给析构了(就好像std::thread有个std::function成员变量,当std::function析构的时候,把closure捕获的变量析构了)。其实不是,捕获的变量的所有权已经迁移到了新线程中了,会在线程执行完了再析构捕获的变量。

当解决完new std::thread之后,就能进一步解决new JSFunction的问题,以及std::vector<cef_v8value_t*>的问题了.

@std-microblock
Copy link
Owner

我又思考了一下new std::thread的问题。 其实可以这么写:

std::thread([=](){

}).detach();

我觉得你使用new的原因是, std::thread在析构的时候会报错,这是因为std::thread析构前必须先joindetach,否则无论如何析构的时候都会报错的,即使线程已经运行结束了。

另外,我一开始没有说应该用detach的原因是,我当时以为std::thread析构的时候,会不会把捕获的变量给析构了(就好像std::thread有个std::function成员变量,当std::function析构的时候,把closure捕获的变量析构了)。其实不是,捕获的变量的所有权已经迁移到了新线程中了,会在线程执行完了再析构捕获的变量。

当解决完new std::thread之后,就能进一步解决new JSFunction的问题,以及std::vector<cef_v8value_t*>的问题了.

std::thread 中 lambda 捕获的变量有所有权转移,会在执行完成后释放,直接 detach 不会导致析构 closure 中捕获的变量(除非是指向局部变量的裸指针之类的)。

这里使用 new std::thread 和 std::thread{}.detach() 的区别只有八个 byte,不是很大,不过依然感谢你的提出。

new JSFunction 确实可以用类似 std::thread 的 detach 思路解决(也就是我前面说的写一个 flag,在执行结束后/当前没有执行任务时自己销毁),不过我感觉还是不是太优雅,我后面再想一下,这边先把这个 new std::thread 改掉先。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants