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

Commit

Permalink
remove dylibs
Browse files Browse the repository at this point in the history
  • Loading branch information
hailang committed Jul 25, 2023
1 parent 2f3dfa2 commit 69d6dfc
Show file tree
Hide file tree
Showing 17 changed files with 111 additions and 149 deletions.
94 changes: 0 additions & 94 deletions .github/workflows/build-hooks.yaml

This file was deleted.

2 changes: 0 additions & 2 deletions .github/workflows/coverage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ name: Code Coverage

on:
push:
branches:
- master
paths-ignore:
- '**.md'
- '**.png'
Expand Down
8 changes: 6 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "open-coroutine"
version = "0.4.0"
version = "0.4.6"
edition = "2021"
authors = ["zhangzicheng@apache.org"]
description = "The open-coroutine is a simple, efficient and generic stackful-coroutine library."
Expand All @@ -12,7 +12,11 @@ license = "LGPL-3.0 OR Apache-2.0"
[dependencies]
libc = "0.2.119"
open-coroutine-core = { version = "0.4.0", path = "open-coroutine-core" }
open-coroutine-macros = { version = "0.1.0", path = "open-coroutine-macros" }
open-coroutine-hooks = { version = "0.4.0", path = "open-coroutine-hooks" }
open-coroutine-macros = { version = "0.1.1", path = "open-coroutine-macros" }

[build-dependencies]
glob = "0.3.1"

[workspace]
members = [
Expand Down
40 changes: 18 additions & 22 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,31 +12,27 @@ fn main() {
.parent()
.unwrap()
.join("deps");
let lib = env::current_dir().unwrap().join("lib");
let mut pattern = deps.to_str().unwrap().to_owned();
if cfg!(target_os = "linux") {
std::fs::copy(
lib.join("libopen_coroutine_hooks.so"),
deps.join("libopen_coroutine_hooks.so"),
)
.expect("copy libopen_coroutine_hooks.so failed!");
pattern += "/libopen_coroutine_hooks*.so";
for path in glob::glob(&pattern)
.expect("Failed to read glob pattern")
.flatten()
{
std::fs::rename(path, deps.join("libopen_coroutine_hooks.so"))
.expect("rename to libopen_coroutine_hooks.so failed!");
}
} else if cfg!(target_os = "macos") {
if cfg!(target_arch = "aarch64") {
std::fs::copy(
lib.join("libopen_coroutine_hooks-m1.dylib"),
deps.join("libopen_coroutine_hooks.dylib"),
)
.expect("copy libopen_coroutine_hooks-m1.dylib failed!");
} else {
std::fs::copy(
lib.join("libopen_coroutine_hooks.dylib"),
deps.join("libopen_coroutine_hooks.dylib"),
)
.expect("copy libopen_coroutine_hooks.dylib failed!");
pattern += "/libopen_coroutine_hooks*.dylib";
for path in glob::glob(&pattern)
.expect("Failed to read glob pattern")
.flatten()
{
std::fs::rename(path, deps.join("libopen_coroutine_hooks.dylib"))
.expect("rename to libopen_coroutine_hooks.dylib failed!");
}
} else if cfg!(target_os = "windows") {
std::fs::copy(lib.join("hook.dll"), deps.join("hook.dll")).expect("copy hook.dll failed!");
std::fs::copy(lib.join("hook.dll.lib"), deps.join("hook.lib"))
.expect("copy hook.lib failed!");
} else {
panic!("unsupported platform");
}
//link hook dylib
println!("cargo:rustc-link-lib=dylib=open_coroutine_hooks");
Expand Down
75 changes: 56 additions & 19 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,14 @@
- [协程窃取](#协程窃取)
- [调度器](#调度器)
- [抢占调度](#抢占调度)
- [协程池](#协程池)
- [EventLoop](#EventLoop)

## 诞生之因

在早期程序员为了支持多个用户并发访问服务应用,往往采用多进程方式,即针对每一个TCP网络连接创建一个服务进程。在2000年左右,比较流行使用CGI方式编写Web服务,当时人们用的比较多的Web服务器是基于多进程模式开发的Apache
1.3.x系列,因为进程占用系统资源较多,而线程占用的资源更少,所以人们开始使用多线程方式编写Web服务应用,这使单台服务器支撑的用户并发度提高了,但依然存在资源浪费的问题。

2020年我入职W公司,由于内部系统不时出现线程池打满的情况,再加上TL读过[《Java线程池实现原理及其在美团业务中的实践》](https://tech.meituan.com/2020/04/02/java-pooling-pratice-in-meituan.html)
,我们决定构建自己的动态线程池,从结果来看,效果不错:

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

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

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

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

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

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

## 调度器

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

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

<div style="text-align: center;">
<img src="img/scheduler.png" width="75%">
<img src="img/scheduler.png" width="80%">
</div>

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

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

## 抢占调度

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

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

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

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

```c++
// 模拟死循环协程体
while (count < 1) {
std::cout << "Waiting for signal..." << std::endl;
sleep(1);
{
// 模拟死循环协程体
while (count < 1) {
std::cout << "Waiting for signal..." << std::endl;
sleep(1);
}
}
```

Expand Down Expand Up @@ -278,7 +291,10 @@ Received signal 2
thread main finished!
```

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

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

Expand Down Expand Up @@ -377,16 +393,37 @@ Waiting for signal...
thread main finished!
```

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

## EventLoop
## 协程池

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

## Hook
1. 资源管理:协程池可以管理协程的创建、销毁和复用。通过使用协程池,可以事先创建好一定数量的协程,并将它们存储在池中供需要时使用。这样可以避免频繁的创建和销毁协程,提高系统的资源利用率。

2. 避免协程饥饿:在使用协程池时,协程会被持续提供任务,避免了协程执行完任务后处于空闲状态的情况。

3. 控制并发度:协程池可以限制并发协程的数量,避免系统因为协程过多而过载。通过设置协程池的大小,可以控制并发度,保证系统资源的合理分配。

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

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

## 发布之后
<div style="text-align: center;">
<img src="img/pool.png" width="100%">
</div>

## EventLoop

传统多进程或多线程编程方式均采用了阻塞编程,这会使得服务端的进程或线程因等待客户端的请求数据而变得空闲,而且在该空闲期间还不能做别的事情,白白浪费了操作系统的调度时间和内存资源。这种一对一的服务方式在广域网的环境下显示变得不够廉价,于是人们开始采用非阻塞网络编程方式来提升网络服务并发度。

event loop核心采用非阻塞IO和事件队列技术,是一种常见的异步编程模型,可以高效地处理多个并发任务。虽然自身为单线程模型,但可轻易通过多线程扩展来提升程序性能。

跨平台方面,目前open-coroutine仅从[mio](https://github.com/tokio-rs/mio)
移植了epoll和kevent,意味着在windows下无法使用;具体操作层面,提供对读写事件的添加/删除/修改/监听(比如epoll_wait)
操作。结合[协程池](#协程池),我们可以轻易地往event
loop中添加非IO任务,然后在监听操作前主动调度这些任务,当然,最后触发监听操作的时间需要减去调度耗时;性能方面,直接内置thread-per-core线程池,并对任务队列前做负载均衡(
由于[协程池](#协程池)[协程窃取](#协程窃取)的存在,即使不做负载均衡也没问题)。

## Hook
Binary file modified docs/img/architecture.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/pool.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed lib/libopen_coroutine_hooks-m1.dylib
Binary file not shown.
Binary file removed lib/libopen_coroutine_hooks.dylib
Binary file not shown.
Binary file removed lib/libopen_coroutine_hooks.so
Binary file not shown.
2 changes: 1 addition & 1 deletion open-coroutine-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ uuid = { version = "1.3.0", features = [
] }
crossbeam-utils = "0.8.15"
crossbeam-deque = "0.8.2"
open-coroutine-timer = { version = "0.0.4", path = "../open-coroutine-timer" }
open-coroutine-timer = { version = "0.1.0", path = "../open-coroutine-timer" }
open-coroutine-queue = { version = "0.1.2", path = "../open-coroutine-queue" }

[target."cfg(windows)".dependencies]
Expand Down
2 changes: 1 addition & 1 deletion open-coroutine-core/src/event_loop/interest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ impl fmt::Debug for Interest {
one = true;
}
}
#[cfg(any(target_os = "freebsd"))]
#[cfg(target_os = "freebsd")]
{
if self.is_lio() {
if one {
Expand Down
4 changes: 2 additions & 2 deletions open-coroutine-core/src/event_loop/selector/kqueue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -563,7 +563,7 @@ pub mod event {
libc::NOTE_REVOKE,
#[cfg(any(target_os = "ios", target_os = "macos"))]
libc::NOTE_NONE,
#[cfg(any(target_os = "openbsd"))]
#[cfg(target_os = "openbsd")]
libc::NOTE_TRUNCATE,
libc::NOTE_EXIT,
libc::NOTE_FORK,
Expand Down Expand Up @@ -615,7 +615,7 @@ pub mod event {
libc::NOTE_VM_ERROR,
#[cfg(any(target_os = "freebsd", target_os = "ios", target_os = "macos"))]
libc::NOTE_SECONDS,
#[cfg(any(target_os = "freebsd"))]
#[cfg(target_os = "freebsd")]
libc::NOTE_MSECONDS,
#[cfg(any(target_os = "freebsd", target_os = "ios", target_os = "macos"))]
libc::NOTE_USECONDS,
Expand Down
6 changes: 2 additions & 4 deletions open-coroutine-hooks/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "open-coroutine-hooks"
version = "0.1.0"
version = "0.4.0"
edition = "2021"
authors = ["zhangzicheng@apache.org"]
description = "The syscall hook for open-coroutine"
Expand All @@ -14,7 +14,7 @@ libc = "0.2.138"
once_cell = "1.13.0"
num_cpus = "1.14.0"
open-coroutine-core = { version = "0.4.0", path = "../open-coroutine-core" }
open-coroutine-timer = { version = "0.0.4", path = "../open-coroutine-timer" }
open-coroutine-timer = { version = "0.1.0", path = "../open-coroutine-timer" }

[target."cfg(windows)".dependencies]
windows-sys = { version = "0.48.0", features = [
Expand All @@ -28,5 +28,3 @@ windows-sys = { version = "0.48.0", features = [

[lib]
crate-type = ["cdylib"]
name = "open_coroutine_hooks"
path = "src/lib.rs"
2 changes: 1 addition & 1 deletion open-coroutine-macros/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "open-coroutine-macros"
version = "0.1.0"
version = "0.1.1"
edition = "2021"
authors = ["zhangzicheng@apache.org"]
description = "The proc macros for open-coroutine"
Expand Down
2 changes: 1 addition & 1 deletion open-coroutine-timer/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "open-coroutine-timer"
version = "0.0.4"
version = "0.1.0"
edition = "2021"
authors = ["zhangzicheng@apache.org"]
description = "The time utils."
Expand Down
Loading

0 comments on commit 69d6dfc

Please sign in to comment.