Skip to content
This repository was archived by the owner on Aug 18, 2023. It is now read-only.

Commit 69d6dfc

Browse files
author
hailang
committed
remove dylibs
1 parent 2f3dfa2 commit 69d6dfc

File tree

17 files changed

+111
-149
lines changed

17 files changed

+111
-149
lines changed

.github/workflows/build-hooks.yaml

Lines changed: 0 additions & 94 deletions
This file was deleted.

.github/workflows/coverage.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ name: Code Coverage
22

33
on:
44
push:
5-
branches:
6-
- master
75
paths-ignore:
86
- '**.md'
97
- '**.png'

Cargo.toml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "open-coroutine"
3-
version = "0.4.0"
3+
version = "0.4.6"
44
edition = "2021"
55
authors = ["[email protected]"]
66
description = "The open-coroutine is a simple, efficient and generic stackful-coroutine library."
@@ -12,7 +12,11 @@ license = "LGPL-3.0 OR Apache-2.0"
1212
[dependencies]
1313
libc = "0.2.119"
1414
open-coroutine-core = { version = "0.4.0", path = "open-coroutine-core" }
15-
open-coroutine-macros = { version = "0.1.0", path = "open-coroutine-macros" }
15+
open-coroutine-hooks = { version = "0.4.0", path = "open-coroutine-hooks" }
16+
open-coroutine-macros = { version = "0.1.1", path = "open-coroutine-macros" }
17+
18+
[build-dependencies]
19+
glob = "0.3.1"
1620

1721
[workspace]
1822
members = [

build.rs

Lines changed: 18 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -12,31 +12,27 @@ fn main() {
1212
.parent()
1313
.unwrap()
1414
.join("deps");
15-
let lib = env::current_dir().unwrap().join("lib");
15+
let mut pattern = deps.to_str().unwrap().to_owned();
1616
if cfg!(target_os = "linux") {
17-
std::fs::copy(
18-
lib.join("libopen_coroutine_hooks.so"),
19-
deps.join("libopen_coroutine_hooks.so"),
20-
)
21-
.expect("copy libopen_coroutine_hooks.so failed!");
17+
pattern += "/libopen_coroutine_hooks*.so";
18+
for path in glob::glob(&pattern)
19+
.expect("Failed to read glob pattern")
20+
.flatten()
21+
{
22+
std::fs::rename(path, deps.join("libopen_coroutine_hooks.so"))
23+
.expect("rename to libopen_coroutine_hooks.so failed!");
24+
}
2225
} else if cfg!(target_os = "macos") {
23-
if cfg!(target_arch = "aarch64") {
24-
std::fs::copy(
25-
lib.join("libopen_coroutine_hooks-m1.dylib"),
26-
deps.join("libopen_coroutine_hooks.dylib"),
27-
)
28-
.expect("copy libopen_coroutine_hooks-m1.dylib failed!");
29-
} else {
30-
std::fs::copy(
31-
lib.join("libopen_coroutine_hooks.dylib"),
32-
deps.join("libopen_coroutine_hooks.dylib"),
33-
)
34-
.expect("copy libopen_coroutine_hooks.dylib failed!");
26+
pattern += "/libopen_coroutine_hooks*.dylib";
27+
for path in glob::glob(&pattern)
28+
.expect("Failed to read glob pattern")
29+
.flatten()
30+
{
31+
std::fs::rename(path, deps.join("libopen_coroutine_hooks.dylib"))
32+
.expect("rename to libopen_coroutine_hooks.dylib failed!");
3533
}
36-
} else if cfg!(target_os = "windows") {
37-
std::fs::copy(lib.join("hook.dll"), deps.join("hook.dll")).expect("copy hook.dll failed!");
38-
std::fs::copy(lib.join("hook.dll.lib"), deps.join("hook.lib"))
39-
.expect("copy hook.lib failed!");
34+
} else {
35+
panic!("unsupported platform");
4036
}
4137
//link hook dylib
4238
println!("cargo:rustc-link-lib=dylib=open_coroutine_hooks");

docs/README.md

Lines changed: 56 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,14 @@
1212
- [协程窃取](#协程窃取)
1313
- [调度器](#调度器)
1414
- [抢占调度](#抢占调度)
15+
- [协程池](#协程池)
16+
- [EventLoop](#EventLoop)
1517

1618
## 诞生之因
1719

20+
在早期程序员为了支持多个用户并发访问服务应用,往往采用多进程方式,即针对每一个TCP网络连接创建一个服务进程。在2000年左右,比较流行使用CGI方式编写Web服务,当时人们用的比较多的Web服务器是基于多进程模式开发的Apache
21+
1.3.x系列,因为进程占用系统资源较多,而线程占用的资源更少,所以人们开始使用多线程方式编写Web服务应用,这使单台服务器支撑的用户并发度提高了,但依然存在资源浪费的问题。
22+
1823
2020年我入职W公司,由于内部系统不时出现线程池打满的情况,再加上TL读过[《Java线程池实现原理及其在美团业务中的实践》](https://tech.meituan.com/2020/04/02/java-pooling-pratice-in-meituan.html)
1924
,我们决定构建自己的动态线程池,从结果来看,效果不错:
2025

@@ -138,7 +143,8 @@ PS:这里解释下hook技术,简单的说,就是函数调用的代理,
138143

139144
暂时采用[corosensei](https://github.com/Amanieu/corosensei),目前正在尝试自研无栈协程。
140145

141-
`suspend``resume`原语直接复用[corosensei](https://github.com/Amanieu/corosensei),这里不过多赘述。 选好底层库,接着就是确定协程的状态了,下面是个人理解:
146+
`suspend``resume`原语直接复用[corosensei](https://github.com/Amanieu/corosensei),这里不过多赘述。
147+
选好底层库,接着就是确定协程的状态了,下面是个人理解:
142148

143149
<div style="text-align: center;">
144150
<img src="img/state.png" width="50%">
@@ -192,37 +198,44 @@ RingBuffer作为最常用的高性能数据结构,主要有几个优点:
192198

193199
无非两种方案,一是先从共享队列取协程,再从其他本地RingBuffer取协程;二是先从其他本地RingBuffer取协程,再从共享队列取协程。怎么选?竞争共享队列等价于竞争一把共享写锁,再竞争其他本地RingBuffer等价于竞争多把共享写锁,从并发冲突的角度考虑,自然是资源越多越好,因此选择方案二。
194200

195-
其他实现细节可参考[《Tokio解析之任务调度》](https://baijiahao.baidu.com/s?id=1746023143258422548),虽然实际用的是[st3](https://github.com/asynchronics/st3),但原理相通。
201+
其他实现细节可参考[《Tokio解析之任务调度》](https://baijiahao.baidu.com/s?id=1746023143258422548)
202+
,虽然实际用的是[st3](https://github.com/asynchronics/st3),但原理相通。
196203

197204
## 调度器
198205

199206
还记得[底层抽象](#底层抽象)里提到的协程状态吗?
200207

201-
我们用[时间轮](#时间轮)来实现suspend队列,基于[协程窃取](#协程窃取)实现ready队列(至于syscall集合,先卖个关子),剩下只要实现submit(往ready队列添加协程)和try_schedule(非阻塞地调度协程)两个方法,就完成了一个功能强大的调度器。
208+
我们用[时间轮](#时间轮)来实现suspend队列,基于[协程窃取](#协程窃取)实现ready队列(至于syscall集合,先卖个关子)
209+
,剩下只要实现submit(往ready队列添加协程)和try_schedule(非阻塞地调度协程)两个方法,就完成了一个功能强大的调度器。
202210

203211
<div style="text-align: center;">
204-
<img src="img/scheduler.png" width="75%">
212+
<img src="img/scheduler.png" width="80%">
205213
</div>
206214

207-
submit方法的实现非常简单,就不阐述了。我们直接谈try_schedule,其实也简单,就是真正调度前,检查一下suspend队列是否有需要运行的协程,如果有则把它加到ready队列,然后调度ready队列的协程就行了(任务窃取算法在[底层](#协程窃取)已经实现了)。
215+
submit方法的实现非常简单,就不阐述了。我们直接谈try_schedule,其实也简单,就是真正调度前,检查一下suspend队列是否有需要运行的协程,如果有则把它加到ready队列,然后调度ready队列的协程就行了(
216+
任务窃取算法在[底层](#协程窃取)已经实现了)。
208217

209218
另外,从扩展性的角度考虑,作者添加了Listener API,当协程创建/挂起/陷入系统调用/完成时,均会回调用户函数,典型适用场景如打日志、监控等等。
210219

211220
## 抢占调度
212221

213222
抢占调度可以让一个正在执行的协程被中断,以便其他等待执行的协程有机会被调度并运行。这种机制可以在遇到阻塞操作或计算密集型任务时及时切换执行其他协程,避免因为一个协程的长时间执行而导致整个程序的性能下降。
214223

215-
在go语言中,抢占调度是通过采用协作式和抢占式混合调度实现的。当协程主动发起I/O操作、调用runtime.Gosched()函数或访问channel等等时,会发生协作式调度,即主动让出CPU并让其他协程执行。而当一个协程超过一定时间限制或发生系统调用等情况时,会发生抢占式调度,即强制剥夺当前协程的执行权。这样的混合调度机制可以在保证程序的高并发性的同时,增加系统的响应能力。
224+
在go语言中,抢占调度是通过采用协作式和抢占式混合调度实现的。当协程主动发起I/O操作、调用runtime.Gosched()
225+
函数或访问channel等等时,会发生协作式调度,即主动让出CPU并让其他协程执行。而当一个协程超过一定时间限制或发生系统调用等情况时,会发生抢占式调度,即强制剥夺当前协程的执行权。这样的混合调度机制可以在保证程序的高并发性的同时,增加系统的响应能力。
216226

217-
为了提高程序的并发性和响应能力,open-coroutine也引入了基于信号的抢占调度机制。与goroutine略微有些差异的是,当发生系统调用时,部分系统调用也会发生协作式调度(先卖个关子,后续再详细介绍)。
227+
为了提高程序的并发性和响应能力,open-coroutine也引入了基于信号的抢占调度机制。与goroutine略微有些差异的是,当发生系统调用时,部分系统调用也会发生协作式调度(
228+
先卖个关子,后续再详细介绍)。
218229

219230
我们把以下代码当成协程体:
220231

221232
```c++
222-
// 模拟死循环协程体
223-
while (count < 1) {
224-
std::cout << "Waiting for signal..." << std::endl;
225-
sleep(1);
233+
{
234+
// 模拟死循环协程体
235+
while (count < 1) {
236+
std::cout << "Waiting for signal..." << std::endl;
237+
sleep(1);
238+
}
226239
}
227240
```
228241

@@ -278,7 +291,10 @@ Received signal 2
278291
thread main finished!
279292
```
280293

281-
解释下,在主线程中,我们开启了一个子线程t1,在注册信号处理函数后,子线程t1将会陷入死循环并输出`Waiting for signal...`到控制台。主线程在睡眠1s后,向子线程t1发送信号,子线程t1的执行权被移交给信号处理函数signal_handler,信号处理函数结束后,子线程t1的执行权回到之前执行的地方(也就是`std::cout << "Waiting for signal..." << std::endl;`下面一行代码)继续执行,此时条件不满足,子线程t1跳出循环,打印`thread main finished!`,子线程t1执行完毕,随后主线程结束等待,也执行完毕。
294+
解释下,在主线程中,我们开启了一个子线程t1,在注册信号处理函数后,子线程t1将会陷入死循环并输出`Waiting for signal...`
295+
到控制台。主线程在睡眠1s后,向子线程t1发送信号,子线程t1的执行权被移交给信号处理函数signal_handler,信号处理函数结束后,子线程t1的执行权回到之前执行的地方(
296+
也就是`std::cout << "Waiting for signal..." << std::endl;`下面一行代码)
297+
继续执行,此时条件不满足,子线程t1跳出循环,打印`thread main finished!`,子线程t1执行完毕,随后主线程结束等待,也执行完毕。
282298

283299
接下来,我们考虑更复杂的情况,即需要重复抢占,修改代码如下:
284300

@@ -377,16 +393,37 @@ Waiting for signal...
377393
thread main finished!
378394
```
379395

380-
上述涉及的系统调用sigemptyset、sigaction、pthread_kill、pthread_sigmask和sigdelset,建议阅读《Linux/UNIX系统编程手册》20~22章节的内容以加深理解。
396+
上述涉及的系统调用sigemptyset、sigaction、pthread_kill、pthread_sigmask和sigdelset,建议阅读《Linux/UNIX系统编程手册》20~
397+
22章节的内容以加深理解。
381398

382-
## EventLoop
399+
## 协程池
383400

384-
## JoinHandle
401+
虽然协程比线程耗费的资源更少,但频繁创建和销毁协程仍然会消耗大量的系统资源,因此将协程池化是必须的。池化后,能带来几个显著优势:
385402

386-
## Hook
403+
1. 资源管理:协程池可以管理协程的创建、销毁和复用。通过使用协程池,可以事先创建好一定数量的协程,并将它们存储在池中供需要时使用。这样可以避免频繁的创建和销毁协程,提高系统的资源利用率。
404+
405+
2. 避免协程饥饿:在使用协程池时,协程会被持续提供任务,避免了协程执行完任务后处于空闲状态的情况。
406+
407+
3. 控制并发度:协程池可以限制并发协程的数量,避免系统因为协程过多而过载。通过设置协程池的大小,可以控制并发度,保证系统资源的合理分配。
387408

388-
## 再次封装
409+
4. 提高代码的可维护性:使用协程池可以将任务的执行和协程的管理分离开来,使代码更加清晰和可维护。任务的执行逻辑可以集中在任务本身,而协程的创建和管理则由协程池来负责。
389410

390-
## 极简属性宏
411+
在open-coroutine中,协程池是惰性的,如果用户不主动调度,任务将不会执行,具体请看下方的流程图:
391412

392-
## 发布之后
413+
<div style="text-align: center;">
414+
<img src="img/pool.png" width="100%">
415+
</div>
416+
417+
## EventLoop
418+
419+
传统多进程或多线程编程方式均采用了阻塞编程,这会使得服务端的进程或线程因等待客户端的请求数据而变得空闲,而且在该空闲期间还不能做别的事情,白白浪费了操作系统的调度时间和内存资源。这种一对一的服务方式在广域网的环境下显示变得不够廉价,于是人们开始采用非阻塞网络编程方式来提升网络服务并发度。
420+
421+
event loop核心采用非阻塞IO和事件队列技术,是一种常见的异步编程模型,可以高效地处理多个并发任务。虽然自身为单线程模型,但可轻易通过多线程扩展来提升程序性能。
422+
423+
跨平台方面,目前open-coroutine仅从[mio](https://github.com/tokio-rs/mio)
424+
移植了epoll和kevent,意味着在windows下无法使用;具体操作层面,提供对读写事件的添加/删除/修改/监听(比如epoll_wait)
425+
操作。结合[协程池](#协程池),我们可以轻易地往event
426+
loop中添加非IO任务,然后在监听操作前主动调度这些任务,当然,最后触发监听操作的时间需要减去调度耗时;性能方面,直接内置thread-per-core线程池,并对任务队列前做负载均衡(
427+
由于[协程池](#协程池)[协程窃取](#协程窃取)的存在,即使不做负载均衡也没问题)。
428+
429+
## Hook

docs/img/architecture.png

616 Bytes
Loading

docs/img/pool.png

79.1 KB
Loading
-729 KB
Binary file not shown.

lib/libopen_coroutine_hooks.dylib

-707 KB
Binary file not shown.

lib/libopen_coroutine_hooks.so

-4.44 MB
Binary file not shown.

0 commit comments

Comments
 (0)