mirror of
https://github.com/tokio-rs/tokio.git
synced 2026-09-09 00:00:08 +02:00
Compare commits
657
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f320197693 | ||
|
|
ea6b144cd1 | ||
|
|
264e703296 | ||
|
|
dfb0f00838 | ||
|
|
4a91f197b0 | ||
|
|
601c383ab6 | ||
|
|
484cb52d8d | ||
|
|
3762a6a990 | ||
|
|
07f6cc7e1d | ||
|
|
308e3e6871 | ||
|
|
5a1879c2d0 | ||
|
|
de6ef21a81 | ||
|
|
90551d234f | ||
|
|
bd4c3dddca | ||
|
|
49b331855e | ||
|
|
da292dfb66 | ||
|
|
b9feac8d68 | ||
|
|
4fee6e3500 | ||
|
|
b1e69e560c | ||
|
|
f020b5c4b5 | ||
|
|
be8ee45b3f | ||
|
|
d9b19166cd | ||
|
|
db8edc620f | ||
|
|
4730984d66 | ||
|
|
1979615cbf | ||
|
|
f669a609cf | ||
|
|
ce41896f8d | ||
|
|
c8ab78a84f | ||
|
|
3911cb8523 | ||
|
|
d545aa2601 | ||
|
|
911ab21d70 | ||
|
|
9e94fa7e15 | ||
|
|
0d234c3cf9 | ||
|
|
3754e059b6 | ||
|
|
6d868d96ce | ||
|
|
0a3fe46086 | ||
|
|
154d7d5fe6 | ||
|
|
611b793356 | ||
|
|
888ee60e41 | ||
|
|
7dd4d8a30e | ||
|
|
085e616c87 | ||
|
|
a7896d07f1 | ||
|
|
aff24dfbeb | ||
|
|
71cc9ab4c2 | ||
|
|
02cbe4591b | ||
|
|
0783797520 | ||
|
|
ab3ff69cf2 | ||
|
|
a0d5b8ab30 | ||
|
|
a1ee3ef218 | ||
|
|
171cd148a3 | ||
|
|
3f1f268583 | ||
|
|
3e890cc017 | ||
|
|
69290a6432 | ||
|
|
e2b175848b | ||
|
|
b7a75b5be3 | ||
|
|
6b705b3053 | ||
|
|
3636fd018a | ||
|
|
2506c9fa99 | ||
|
|
b3a14483bf | ||
|
|
013f323def | ||
|
|
b926700065 | ||
|
|
99a03a502e | ||
|
|
933fa498d0 | ||
|
|
9f848c9f54 | ||
|
|
912b862a05 | ||
|
|
714e5b571f | ||
|
|
8e999e3806 | ||
|
|
1d980145cb | ||
|
|
8259133ca0 | ||
|
|
38d88c6799 | ||
|
|
c38de96b94 | ||
|
|
2440d113ff | ||
|
|
ab8d7b82a1 | ||
|
|
9563707aaa | ||
|
|
193c1574a1 | ||
|
|
328bd049f6 | ||
|
|
4380de9fe9 | ||
|
|
98f527f42d | ||
|
|
3768696d92 | ||
|
|
d7d4f7d08b | ||
|
|
421a7b001c | ||
|
|
b1bdb3c57b | ||
|
|
7ec77a0677 | ||
|
|
55e3ed2a39 | ||
|
|
17d8c2b29d | ||
|
|
327bec2caf | ||
|
|
ea30a5ea5e | ||
|
|
0cf95f0673 | ||
|
|
a48e418dcb | ||
|
|
4cbcb687f4 | ||
|
|
0715e6defc | ||
|
|
bdd64cc9d3 | ||
|
|
f0fdef80c4 | ||
|
|
00754c8f9c | ||
|
|
1ae9434e8e | ||
|
|
8895bba448 | ||
|
|
48ca254d92 | ||
|
|
a0af02a396 | ||
|
|
0ce3a1188a | ||
|
|
1ea9ce11d4 | ||
|
|
4d4d12613b | ||
|
|
5490267a79 | ||
|
|
1434b32b5a | ||
|
|
159a3b2c85 | ||
|
|
ce87dcfbf0 | ||
|
|
d41d49d202 | ||
|
|
7a6c424f6e | ||
|
|
c3037adac9 | ||
|
|
964fd06e0f | ||
|
|
817fa605ee | ||
|
|
77de684ed9 | ||
|
|
83d550e511 | ||
|
|
1b3d3e7cd6 | ||
|
|
9e044e144b | ||
|
|
cb08fbc6c3 | ||
|
|
e59584a661 | ||
|
|
f7fb0bdc7a | ||
|
|
9faea740df | ||
|
|
2a8c551631 | ||
|
|
676630785b | ||
|
|
ec4b1d7215 | ||
|
|
e3c3a56718 | ||
|
|
a7b658c35b | ||
|
|
c1c8d1033d | ||
|
|
aa303bc205 | ||
|
|
7b6ccb515f | ||
|
|
4b174ce2c9 | ||
|
|
0ec4d0db4d | ||
|
|
d83ba30d8d | ||
|
|
f339587b27 | ||
|
|
b663abe091 | ||
|
|
9a11efc262 | ||
|
|
d760b26666 | ||
|
|
d413c9c02a | ||
|
|
addbfb9204 | ||
|
|
5687043328 | ||
|
|
72c87a7724 | ||
|
|
8507e28f89 | ||
|
|
7efcab43c9 | ||
|
|
e4a39d2ef6 | ||
|
|
afd3678f89 | ||
|
|
8182ecf262 | ||
|
|
a258bff701 | ||
|
|
e076d21f67 | ||
|
|
042433cdcc | ||
|
|
0284d1b5c8 | ||
|
|
710bc8071e | ||
|
|
a2b12bd579 | ||
|
|
e7b593cbee | ||
|
|
3aaf4a5377 | ||
|
|
8e741c1c0e | ||
|
|
47d46455bd | ||
|
|
20c1fdc678 | ||
|
|
638ce93591 | ||
|
|
c853991b1e | ||
|
|
6d410f6c90 | ||
|
|
a27575f284 | ||
|
|
13fbdace66 | ||
|
|
4380c3d821 | ||
|
|
383da87313 | ||
|
|
17117b591e | ||
|
|
aa70f6c5f0 | ||
|
|
67c343d9e9 | ||
|
|
34cdcc7d87 | ||
|
|
8e134172dd | ||
|
|
605ef578df | ||
|
|
9b578f0c9d | ||
|
|
0a15768380 | ||
|
|
eb1a2ee990 | ||
|
|
8713d39228 | ||
|
|
7e27911911 | ||
|
|
4b3da20c98 | ||
|
|
b8ac94ed70 | ||
|
|
5086e56dcb | ||
|
|
2671ffb55b | ||
|
|
7f09959b0a | ||
|
|
fb7dec0e95 | ||
|
|
ee19b0ed73 | ||
|
|
c081dfe3ce | ||
|
|
21a13f9eea | ||
|
|
a82bdeebe9 | ||
|
|
435e39001b | ||
|
|
dabae570b1 | ||
|
|
6bd3be2e45 | ||
|
|
6fc1a8c8da | ||
|
|
5c8cd33820 | ||
|
|
5f3296df77 | ||
|
|
cc974a646b | ||
|
|
15495fd883 | ||
|
|
ad4183412a | ||
|
|
bd3e857737 | ||
|
|
15f73666f1 | ||
|
|
fd2048dad1 | ||
|
|
e8f39157b6 | ||
|
|
67f127769b | ||
|
|
463502cbaf | ||
|
|
a1520f5525 | ||
|
|
acd6627d6d | ||
|
|
2353806daf | ||
|
|
7be2bfa744 | ||
|
|
e066431c94 | ||
|
|
2052938a9f | ||
|
|
b3ff911c38 | ||
|
|
9d42b977df | ||
|
|
970d880ceb | ||
|
|
4ca13e6015 | ||
|
|
b54b9d4338 | ||
|
|
10e23d1c62 | ||
|
|
aa7e0cef72 | ||
|
|
bfa8cadaa0 | ||
|
|
6d15c6cacb | ||
|
|
79a2afae9f | ||
|
|
48e07a6d10 | ||
|
|
eb72ddde3b | ||
|
|
dc16b12edb | ||
|
|
67355c6d23 | ||
|
|
405d746d38 | ||
|
|
e0d1293fac | ||
|
|
480c010b01 | ||
|
|
c032ea0203 | ||
|
|
0b31c2f73d | ||
|
|
129f9fc0c8 | ||
|
|
b5c227d51f | ||
|
|
dcae2b9eb8 | ||
|
|
bb9d57017e | ||
|
|
af9c683d52 | ||
|
|
4bc5a1a058 | ||
|
|
f8948ea021 | ||
|
|
bce9780dd3 | ||
|
|
38151f30cb | ||
|
|
5dda72d338 | ||
|
|
c07257f99f | ||
|
|
d08578fc9a | ||
|
|
4047d7962a | ||
|
|
cbdceb91ac | ||
|
|
d4178cf349 | ||
|
|
2f899144ed | ||
|
|
6255598baa | ||
|
|
772e0ca8a6 | ||
|
|
3b677d1fde | ||
|
|
bb7ca7507b | ||
|
|
4a34b77af5 | ||
|
|
8897885425 | ||
|
|
0dbdd196b6 | ||
|
|
94e55c092b | ||
|
|
4468f27c31 | ||
|
|
070a825999 | ||
|
|
946401c345 | ||
|
|
0c01fd23b4 | ||
|
|
ebe241647e | ||
|
|
01e04daaa1 | ||
|
|
92ccadeb3c | ||
|
|
fbfeb9a68a | ||
|
|
da745ff335 | ||
|
|
ce1c74f1cc | ||
|
|
28c9a14a2e | ||
|
|
32e0b4325f | ||
|
|
1656d8e231 | ||
|
|
c9e998e4b3 | ||
|
|
512e9decfb | ||
|
|
5ada5114df | ||
|
|
161b8c80d5 | ||
|
|
9cc4a81678 | ||
|
|
679d7657dc | ||
|
|
c3a935541d | ||
|
|
29cd6ec1ec | ||
|
|
b68f5c7f38 | ||
|
|
6c5dbfa08c | ||
|
|
2c14f88c90 | ||
|
|
e2e1e8e71d | ||
|
|
21df16d759 | ||
|
|
c8af499990 | ||
|
|
623928e371 | ||
|
|
09bc9a05e4 | ||
|
|
82628b8a78 | ||
|
|
21cf5a5469 | ||
|
|
8ef5163df8 | ||
|
|
542197cdb9 | ||
|
|
a302367b8f | ||
|
|
b5de84d19b | ||
|
|
02aaea28b9 | ||
|
|
83e922f051 | ||
|
|
a2496548d1 | ||
|
|
0cea36fa3d | ||
|
|
d6213594ca | ||
|
|
91169992b2 | ||
|
|
8046a87a99 | ||
|
|
5dcc848fc8 | ||
|
|
bd4ccae184 | ||
|
|
4ed0fa21e4 | ||
|
|
12b2567b95 | ||
|
|
35f244ad09 | ||
|
|
1166ecc2ac | ||
|
|
27539ae3bd | ||
|
|
ea6d652a10 | ||
|
|
11f66f43a0 | ||
|
|
479a56a010 | ||
|
|
b37f0de28a | ||
|
|
c9fad08466 | ||
|
|
cc70a211ad | ||
|
|
1ac8dff213 | ||
|
|
ff3f2a8878 | ||
|
|
5b9a290acd | ||
|
|
70569bd009 | ||
|
|
9bd6702a3f | ||
|
|
365269adaf | ||
|
|
3d439ab711 | ||
|
|
b2ea40bb54 | ||
|
|
5ea3c63d7e | ||
|
|
56f3f40c15 | ||
|
|
2d697fc92b | ||
|
|
39c3c19bbd | ||
|
|
694577fa85 | ||
|
|
17819062e2 | ||
|
|
6ad1912353 | ||
|
|
a491b16a89 | ||
|
|
0ecf5f0f03 | ||
|
|
1e798d26ed | ||
|
|
ab53bf0c47 | ||
|
|
338e13b04b | ||
|
|
1077b0b29d | ||
|
|
0cbf1a5ada | ||
|
|
04c2718508 | ||
|
|
ebda4c3d3f | ||
|
|
f602eae499 | ||
|
|
438def7957 | ||
|
|
ee8d4d1b05 | ||
|
|
3297052763 | ||
|
|
f8fe0ffb23 | ||
|
|
47210a8e6e | ||
|
|
29545d9037 | ||
|
|
48e35c11d9 | ||
|
|
dd1d37167d | ||
|
|
6a1a7b1591 | ||
|
|
51b03f0334 | ||
|
|
754a1fb03c | ||
|
|
90b23a9584 | ||
|
|
b69f16aa21 | ||
|
|
6e845b794d | ||
|
|
9681ce2b95 | ||
|
|
feb742c58e | ||
|
|
15cd5146d4 | ||
|
|
56f4bc6543 | ||
|
|
3ad5b6df1a | ||
|
|
1be8a8e691 | ||
|
|
da17c61464 | ||
|
|
f71bded943 | ||
|
|
fc058b9561 | ||
|
|
c0280624f3 | ||
|
|
1e286689ff | ||
|
|
14b9f71157 | ||
|
|
24344dfe4b | ||
|
|
15925efe43 | ||
|
|
4825c444eb | ||
|
|
c8f3539bc1 | ||
|
|
b2e4c5fc9e | ||
|
|
4d0d89fb70 | ||
|
|
fe7285d3d1 | ||
|
|
dff4ecd0e7 | ||
|
|
68d0e3cb5f | ||
|
|
65d0e08d39 | ||
|
|
06582776a5 | ||
|
|
ed4ddf443d | ||
|
|
9a75d6f7f7 | ||
|
|
3bf4f93854 | ||
|
|
39cf6bba00 | ||
|
|
8480a180e6 | ||
|
|
53ea44bfb9 | ||
|
|
a865ca139a | ||
|
|
479f736935 | ||
|
|
17555d71d9 | ||
|
|
341b5daa6e | ||
|
|
53b586c5b9 | ||
|
|
833ee027d0 | ||
|
|
126ce89bb4 | ||
|
|
8e15c234c6 | ||
|
|
16fccafb41 | ||
|
|
3f397ccded | ||
|
|
49609d073f | ||
|
|
a91d43823c | ||
|
|
8fca6f6dad | ||
|
|
75c953bd63 | ||
|
|
14c17fc096 | ||
|
|
65cbf730de | ||
|
|
dbf93c7184 | ||
|
|
873cb8ae2f | ||
|
|
97bb47b480 | ||
|
|
86658bd87d | ||
|
|
9e00b266e0 | ||
|
|
6c42d286b3 | ||
|
|
3a6fdc0568 | ||
|
|
2890d0c3db | ||
|
|
0a85a9662d | ||
|
|
12920cea45 | ||
|
|
cba86cf1b1 | ||
|
|
16ef7b1fd5 | ||
|
|
1914e1e4b9 | ||
|
|
e62c3e9269 | ||
|
|
0b651c070f | ||
|
|
a02407171a | ||
|
|
daa89017da | ||
|
|
35c6fd9924 | ||
|
|
d221c500fa | ||
|
|
ced7739f69 | ||
|
|
18e048d764 | ||
|
|
df77063485 | ||
|
|
d085260ee0 | ||
|
|
227979f091 | ||
|
|
6fcd9c0217 | ||
|
|
2a0df5fb05 | ||
|
|
b652a4e64c | ||
|
|
7f59a6ea85 | ||
|
|
75e5b3d96d | ||
|
|
cdf9d997dc | ||
|
|
f6eb1ee196 | ||
|
|
b7d4fba707 | ||
|
|
3c8d8e60ca | ||
|
|
e971a5e7d7 | ||
|
|
28439e2269 | ||
|
|
d33fdd86a3 | ||
|
|
731dde21dc | ||
|
|
9ed595767d | ||
|
|
a73d6bf33a | ||
|
|
8093712604 | ||
|
|
19618905de | ||
|
|
2438b43671 | ||
|
|
125a185749 | ||
|
|
f3bfed30eb | ||
|
|
5ba12a7145 | ||
|
|
9c6ff3b7e5 | ||
|
|
1f6fc55917 | ||
|
|
a5bd0deaa5 | ||
|
|
ee68c1a8c2 | ||
|
|
90e20bcad3 | ||
|
|
bdf4c142c9 | ||
|
|
43de364cd9 | ||
|
|
224fea4f3c | ||
|
|
ccee1d4493 | ||
|
|
be9328da75 | ||
|
|
b6d74ac4eb | ||
|
|
5712aaf995 | ||
|
|
431b7c5263 | ||
|
|
01ed7b55f7 | ||
|
|
035a968bdd | ||
|
|
a1acfd8c20 | ||
|
|
328a02c1ce | ||
|
|
e9ae5d4ce9 | ||
|
|
d298049299 | ||
|
|
1fcb77db34 | ||
|
|
3936ebdfe4 | ||
|
|
9c337ca1a3 | ||
|
|
e542501439 | ||
|
|
4601c84718 | ||
|
|
deff2524c3 | ||
|
|
4565b81097 | ||
|
|
3ce4720a45 | ||
|
|
8342e4b524 | ||
|
|
4c453e9790 | ||
|
|
1846483f19 | ||
|
|
baad270b98 | ||
|
|
f9d78fbe92 | ||
|
|
bb25a06f34 | ||
|
|
d51f16855b | ||
|
|
5baa8d58ec | ||
|
|
bd51feac47 | ||
|
|
b2896feb5b | ||
|
|
3d0d0fd2af | ||
|
|
7cfb100796 | ||
|
|
e37bd63854 | ||
|
|
c9e75785c8 | ||
|
|
3141ed6228 | ||
|
|
ea1cfbdb97 | ||
|
|
fb2dc97468 | ||
|
|
a3d2548789 | ||
|
|
b4ab6472de | ||
|
|
3133af42e1 | ||
|
|
1f924f95f1 | ||
|
|
f6d061919f | ||
|
|
f5ca423bf1 | ||
|
|
e0d33c4a20 | ||
|
|
3125279697 | ||
|
|
5658d7c503 | ||
|
|
a2096049ee | ||
|
|
94db07b379 | ||
|
|
099ee23b65 | ||
|
|
b32826bc93 | ||
|
|
2ce1cee0f9 | ||
|
|
84e41d4aff | ||
|
|
db6929ad62 | ||
|
|
e25d661e08 | ||
|
|
e392c4ff1e | ||
|
|
0fbde0e94b | ||
|
|
10c9eeb6c2 | ||
|
|
47a5fe3a12 | ||
|
|
fbdf539ac2 | ||
|
|
63caced26f | ||
|
|
0b31b2a195 | ||
|
|
cb197def68 | ||
|
|
eaf81ed324 | ||
|
|
53f9e5a357 | ||
|
|
9077762545 | ||
|
|
131e7b4e49 | ||
|
|
e53b92a993 | ||
|
|
7536132065 | ||
|
|
b6d0c9091d | ||
|
|
4846959e8a | ||
|
|
ec3038357f | ||
|
|
f80bbec28f | ||
|
|
eab26a662c | ||
|
|
58edfc61ab | ||
|
|
bfd7b08067 | ||
|
|
12ce924fb9 | ||
|
|
e4f9bcb577 | ||
|
|
8463af92af | ||
|
|
84c5674c60 | ||
|
|
3275cfb638 | ||
|
|
9780bf491f | ||
|
|
d6ba535ceb | ||
|
|
e929d0e8b9 | ||
|
|
2f730d4d5a | ||
|
|
c7e7f203ee | ||
|
|
7c606ab44a | ||
|
|
2d2faf6014 | ||
|
|
c029771247 | ||
|
|
7341004535 | ||
|
|
02b779e315 | ||
|
|
48345d6e48 | ||
|
|
581cd41d79 | ||
|
|
5f7fe8fd0d | ||
|
|
52f28dcb4f | ||
|
|
d51308b035 | ||
|
|
7cae89af47 | ||
|
|
53ad102cf0 | ||
|
|
e7214e3670 | ||
|
|
433545782e | ||
|
|
bd15bace35 | ||
|
|
410660fd54 | ||
|
|
46ff36386d | ||
|
|
0a1695ebfb | ||
|
|
4aa7bbff4c | ||
|
|
c06a55088b | ||
|
|
92a3455c66 | ||
|
|
1968565825 | ||
|
|
c9273f1aee | ||
|
|
e05d0f8c2c | ||
|
|
debcb2254a | ||
|
|
83b7397e44 | ||
|
|
3991f9f9a4 | ||
|
|
48c0e6283f | ||
|
|
d561b5850a | ||
|
|
3a4aef17b2 | ||
|
|
a0a58d7edd | ||
|
|
ed4f766c98 | ||
|
|
3ac4cfb68a | ||
|
|
7232424a81 | ||
|
|
4c33ed33f6 | ||
|
|
3468b4b72f | ||
|
|
2400769b54 | ||
|
|
a8e8fa6681 | ||
|
|
498288cd31 | ||
|
|
7a30504fd4 | ||
|
|
340d4e5238 | ||
|
|
7b555185ff | ||
|
|
135d7ca38e | ||
|
|
e6720f985d | ||
|
|
d44e995bb0 | ||
|
|
06660ef00a | ||
|
|
2e5773a6fe | ||
|
|
49eb26f159 | ||
|
|
19d96c0674 | ||
|
|
30b2eb17c8 | ||
|
|
8ec3e0d94d | ||
|
|
161ecec156 | ||
|
|
61fcc3bc0b | ||
|
|
944024e8eb | ||
|
|
65f861f478 | ||
|
|
4c8580152d | ||
|
|
ed32cd194c | ||
|
|
593dbf55d1 | ||
|
|
d8a4a5f24b | ||
|
|
cc86fef9c0 | ||
|
|
f3949cc56d | ||
|
|
503fad7908 | ||
|
|
58acb56a17 | ||
|
|
d22c549d97 | ||
|
|
bc48a6fa8d | ||
|
|
70410836ae | ||
|
|
881b510a07 | ||
|
|
6871084629 | ||
|
|
1b8ebfcffb | ||
|
|
654a3d5acf | ||
|
|
a08ad926b1 | ||
|
|
723934242b | ||
|
|
944f769cd5 | ||
|
|
f3ad6cffd9 | ||
|
|
f1e41a4ad4 | ||
|
|
f9335b8186 | ||
|
|
1134cbb168 | ||
|
|
339c78a680 | ||
|
|
c00861210b | ||
|
|
0f296d2089 | ||
|
|
d420d528f9 | ||
|
|
0457690d01 | ||
|
|
4557451257 | ||
|
|
2bd43765d9 | ||
|
|
f306bd02c3 | ||
|
|
6b010ac80f | ||
|
|
8cd3383913 | ||
|
|
d6ed00c292 | ||
|
|
5d29136a83 | ||
|
|
52b29b33bb | ||
|
|
eaba9712e8 | ||
|
|
310adf7ca6 | ||
|
|
0700d6a7cd | ||
|
|
ca89c5b2ec | ||
|
|
453c720709 | ||
|
|
e76c06ba38 | ||
|
|
02aacf5110 | ||
|
|
707fb4d0df | ||
|
|
b161633b5f | ||
|
|
f5b8cf9dac | ||
|
|
aa36807c02 | ||
|
|
74c7a87985 | ||
|
|
9bc782acfc | ||
|
|
3f6165d82e | ||
|
|
ad7f988da3 | ||
|
|
9d51b76d01 | ||
|
|
804511822b | ||
|
|
e6553c4ee3 | ||
|
|
d247e7f5df | ||
|
|
65e7715909 | ||
|
|
61042b4d90 | ||
|
|
1c428cc558 | ||
|
|
61f095fdc1 | ||
|
|
65027b60bc | ||
|
|
b046c0dcbb | ||
|
|
737dff40cb | ||
|
|
a6be73eecb | ||
|
|
9fafe783d3 | ||
|
|
fb3ae0a254 | ||
|
|
aad1892ab5 | ||
|
|
8ea303e027 | ||
|
|
84ed35ef70 | ||
|
|
95fb599664 | ||
|
|
8b312ee571 | ||
|
|
fd7d0ad5e5 | ||
|
|
37bb47c4a2 | ||
|
|
cb1e10b745 | ||
|
|
fb3028f3a2 | ||
|
|
b45f5831cf | ||
|
|
0fe24fcffa | ||
|
|
d1dae25cd2 | ||
|
|
59c9364689 | ||
|
|
3b79be624d | ||
|
|
8955ed5f85 | ||
|
|
bc26934e3b | ||
|
|
3d64a06600 |
@@ -1,8 +0,0 @@
|
||||
# See https://github.com/rustsec/rustsec/blob/59e1d2ad0b9cbc6892c26de233d4925074b4b97b/cargo-audit/audit.toml.example for example.
|
||||
|
||||
[advisories]
|
||||
ignore = [
|
||||
# We depend on nix 0.22 only via mio-aio, a dev-dependency.
|
||||
# https://github.com/tokio-rs/tokio/pull/4255#issuecomment-974786349
|
||||
"RUSTSEC-2021-0119",
|
||||
]
|
||||
@@ -1,25 +0,0 @@
|
||||
version: 2.1
|
||||
jobs:
|
||||
test-arm:
|
||||
machine:
|
||||
image: ubuntu-2004:202101-01
|
||||
resource_class: arm.medium
|
||||
environment:
|
||||
# Change to pin rust version
|
||||
RUST_STABLE: stable
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Install Rust
|
||||
command: |
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs -o rustup.sh
|
||||
chmod +x rustup.sh
|
||||
./rustup.sh -y --default-toolchain $RUST_STABLE
|
||||
source "$HOME"/.cargo/env
|
||||
# Only run Tokio tests
|
||||
- run: cargo test --all-features -p tokio
|
||||
|
||||
workflows:
|
||||
ci:
|
||||
jobs:
|
||||
- test-arm
|
||||
+2
-2
@@ -1,10 +1,10 @@
|
||||
only_if: $CIRRUS_TAG == '' && ($CIRRUS_PR != '' || $CIRRUS_BRANCH == 'master' || $CIRRUS_BRANCH =~ 'tokio-.*')
|
||||
auto_cancellation: $CIRRUS_BRANCH != 'master' && $CIRRUS_BRANCH !=~ 'tokio-.*'
|
||||
freebsd_instance:
|
||||
image_family: freebsd-13-1
|
||||
image_family: freebsd-14-3
|
||||
env:
|
||||
RUST_STABLE: stable
|
||||
RUST_NIGHTLY: nightly-2022-10-25
|
||||
RUST_NIGHTLY: nightly-2025-01-25
|
||||
RUSTFLAGS: -D warnings
|
||||
|
||||
# Test FreeBSD in a full VM on cirrus-ci.com. Test the i686 target too, in the
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
msrv = "1.56"
|
||||
@@ -0,0 +1,20 @@
|
||||
# Buildomat illumos CI
|
||||
|
||||
This directory contains CI configurations for the [illumos] operating system.
|
||||
Tokio's illumos CI jobs are run using [Buildomat], a CI system developed by
|
||||
Oxide Computer, which supports illumos. See [the Buildomat README] for more
|
||||
details.
|
||||
|
||||
## illumos-Specific CI Failures
|
||||
|
||||
If your pull request's CI build fails on illumos, and you aren't able to easily
|
||||
reproduce the failure on other operating systems, don't worry! The
|
||||
[tokio-rs/illumos] team is responsible for maintaining Tokio's illumos support,
|
||||
and can be called on to assist contributors with illumos-specific issues. Please
|
||||
feel free to tag @tokio-rs/illumos to ask for help resolving build failures on
|
||||
illumos.
|
||||
|
||||
[illumos]: https://www.illumos.org/
|
||||
[Buildomat]: https://github.com/oxidecomputer/buildomat
|
||||
[the Buildomat README]: https://github.com/oxidecomputer/buildomat
|
||||
[tokio-rs/illumos]: https://github.com/orgs/tokio-rs/teams/illumos
|
||||
@@ -0,0 +1,8 @@
|
||||
# Repository-level Buildomat configuration.
|
||||
# See: https://github.com/oxidecomputer/buildomat#per-repository-configuration
|
||||
|
||||
# Enable buildomat. This one should be self-explanatory.
|
||||
enable = true
|
||||
# Allow CI runs for PRs from users outside the `tokio-rs` organization. Our
|
||||
# buildomat jobs don't touch any secrets/keys, so this should be fine.
|
||||
org_only = false
|
||||
@@ -19,10 +19,3 @@ R-loom-multi-thread:
|
||||
- tokio/src/runtime/scheduler/multi_thread/**
|
||||
- tokio/src/runtime/task/*
|
||||
- tokio/src/runtime/task/**
|
||||
|
||||
R-loom-multi-thread-alt:
|
||||
- tokio/src/runtime/scheduler/*
|
||||
- tokio/src/runtime/scheduler/multi_thread_alt/*
|
||||
- tokio/src/runtime/scheduler/multi_thread_alt/**
|
||||
- tokio/src/runtime/task/*
|
||||
- tokio/src/runtime/task/**
|
||||
|
||||
@@ -13,18 +13,12 @@ permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
security-audit:
|
||||
cargo-deny:
|
||||
permissions:
|
||||
checks: write # for rustsec/audit-check to create check
|
||||
contents: read # for actions/checkout to fetch code
|
||||
issues: write # for rustsec/audit-check to create issues
|
||||
checks: write
|
||||
contents: read
|
||||
issues: write
|
||||
runs-on: ubuntu-latest
|
||||
if: "!contains(github.event.head_commit.message, 'ci skip')"
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Audit Check
|
||||
# https://github.com/rustsec/audit-check/issues/2
|
||||
uses: rustsec/audit-check@master
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
- uses: actions/checkout@v4
|
||||
- uses: EmbarkStudios/cargo-deny-action@v2
|
||||
|
||||
+396
-77
@@ -13,10 +13,13 @@ concurrency:
|
||||
env:
|
||||
RUSTFLAGS: -Dwarnings
|
||||
RUST_BACKTRACE: 1
|
||||
RUSTUP_WINDOWS_PATH_ADD_BIN: 1
|
||||
# Change to specific Rust release to pin
|
||||
rust_stable: stable
|
||||
rust_nightly: nightly-2023-05-18
|
||||
rust_clippy: 1.65.0
|
||||
rust_nightly: nightly-2025-01-25
|
||||
# Pin a specific miri version
|
||||
rust_miri_nightly: nightly-2025-06-02
|
||||
rust_clippy: '1.88'
|
||||
# When updating this, also update:
|
||||
# - README.md
|
||||
# - tokio/README.md
|
||||
@@ -25,7 +28,7 @@ env:
|
||||
# - tokio-util/Cargo.toml
|
||||
# - tokio-test/Cargo.toml
|
||||
# - tokio-stream/Cargo.toml
|
||||
rust_min: 1.63.0
|
||||
rust_min: '1.70'
|
||||
|
||||
defaults:
|
||||
run:
|
||||
@@ -46,9 +49,12 @@ jobs:
|
||||
- test-parking_lot
|
||||
- valgrind
|
||||
- test-unstable
|
||||
- miri
|
||||
- miri-lib
|
||||
- miri-test
|
||||
- miri-doc
|
||||
- asan
|
||||
- cross-check
|
||||
- cross-check-tier3
|
||||
- cross-test-with-parking_lot
|
||||
- cross-test-without-parking_lot
|
||||
- no-atomic-u64-test
|
||||
@@ -62,13 +68,15 @@ jobs:
|
||||
- loom-compile
|
||||
- check-readme
|
||||
- test-hyper
|
||||
- test-quinn
|
||||
- x86_64-fortanix-unknown-sgx
|
||||
- check-redox
|
||||
- wasm32-unknown-unknown
|
||||
- wasm32-wasi
|
||||
- wasm32-wasip1
|
||||
- check-external-types
|
||||
- check-fuzzing
|
||||
- check-unstable-mt-counters
|
||||
- check-spelling
|
||||
steps:
|
||||
- run: exit 0
|
||||
|
||||
@@ -95,7 +103,7 @@ jobs:
|
||||
- ubuntu-latest
|
||||
- macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install Rust ${{ env.rust_stable }}
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
@@ -127,7 +135,7 @@ jobs:
|
||||
- ubuntu-latest
|
||||
- macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install Rust ${{ env.rust_stable }}
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
@@ -146,6 +154,34 @@ jobs:
|
||||
cargo nextest run --workspace --all-features
|
||||
cargo test --doc --workspace --all-features
|
||||
|
||||
test-workspace-all-features-panic-abort:
|
||||
needs: basics
|
||||
name: test all crates in the workspace with all features and panic=abort
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os:
|
||||
- windows-latest
|
||||
- ubuntu-latest
|
||||
- macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install Rust ${{ env.rust_nightly }}
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: ${{ env.rust_nightly }}
|
||||
- name: Install cargo-nextest
|
||||
uses: taiki-e/install-action@v2
|
||||
with:
|
||||
tool: cargo-nextest
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
- name: test all --all-features panic=abort
|
||||
run: |
|
||||
set -euxo pipefail
|
||||
RUSTFLAGS="$RUSTFLAGS -C panic=abort -Zpanic-abort-tests" cargo nextest run --workspace --exclude tokio-macros --exclude tests-build --all-features --tests
|
||||
|
||||
test-integration-tests-per-feature:
|
||||
needs: basics
|
||||
name: Run integration tests for each feature
|
||||
@@ -157,7 +193,7 @@ jobs:
|
||||
- ubuntu-latest
|
||||
- macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install Rust ${{ env.rust_stable }}
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
@@ -179,11 +215,10 @@ jobs:
|
||||
run: cargo hack test --each-feature
|
||||
working-directory: tests-build
|
||||
|
||||
# Check benchmarks. Run of benchmarks is done by bench.yml workflow.
|
||||
# Check benchmarks.
|
||||
- name: Check benches
|
||||
run: cargo check --benches
|
||||
working-directory: benches
|
||||
# bench.yml workflow runs benchmarks only on linux.
|
||||
if: startsWith(matrix.os, 'ubuntu')
|
||||
|
||||
test-parking_lot:
|
||||
@@ -198,7 +233,7 @@ jobs:
|
||||
needs: basics
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install Rust ${{ env.rust_stable }}
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
@@ -217,11 +252,11 @@ jobs:
|
||||
needs: basics
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install Rust ${{ env.rust_stable }}
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: ${{ env.rust_stable }}
|
||||
toolchain: 1.82
|
||||
|
||||
- name: Install Valgrind
|
||||
uses: taiki-e/install-action@valgrind
|
||||
@@ -256,7 +291,7 @@ jobs:
|
||||
- os: ubuntu-latest
|
||||
- os: macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install Rust ${{ env.rust_stable }}
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
@@ -290,7 +325,7 @@ jobs:
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install Rust ${{ env.rust_stable }}
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
@@ -315,6 +350,39 @@ jobs:
|
||||
# the unstable cfg to RustDoc
|
||||
RUSTDOCFLAGS: --cfg tokio_unstable --cfg tokio_taskdump
|
||||
|
||||
test-uring:
|
||||
name: test tokio full --cfg tokio_uring
|
||||
needs: basics
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install Rust ${{ env.rust_stable }}
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: ${{ env.rust_stable }}
|
||||
|
||||
- name: Install cargo-nextest
|
||||
uses: taiki-e/install-action@v2
|
||||
with:
|
||||
tool: cargo-nextest
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: test tokio full --cfg tokio_uring
|
||||
run: |
|
||||
set -euxo pipefail
|
||||
cargo nextest run --all-features
|
||||
cargo test --doc --all-features
|
||||
working-directory: tokio
|
||||
env:
|
||||
RUSTFLAGS: --cfg tokio_uring -Dwarnings
|
||||
# in order to run doctests for unstable features, we must also pass
|
||||
# the unstable cfg to RustDoc
|
||||
RUSTDOCFLAGS: --cfg tokio_uring
|
||||
|
||||
check-unstable-mt-counters:
|
||||
name: check tokio full --internal-mt-counters
|
||||
needs: basics
|
||||
@@ -324,7 +392,7 @@ jobs:
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install Rust ${{ env.rust_stable }}
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
@@ -347,23 +415,67 @@ jobs:
|
||||
# the unstable cfg to RustDoc
|
||||
RUSTDOCFLAGS: --cfg tokio_unstable --cfg tokio_internal_mt_counters
|
||||
|
||||
miri:
|
||||
name: miri
|
||||
miri-lib:
|
||||
name: miri-lib
|
||||
needs: basics
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install Rust ${{ env.rust_nightly }}
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install Rust ${{ env.rust_miri_nightly }}
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: ${{ env.rust_nightly }}
|
||||
toolchain: ${{ env.rust_miri_nightly }}
|
||||
components: miri
|
||||
- name: Install cargo-nextest
|
||||
uses: taiki-e/install-action@v2
|
||||
with:
|
||||
tool: cargo-nextest
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: miri
|
||||
# Many of tests in tokio/tests and doctests use #[tokio::test] or
|
||||
# #[tokio::main] that calls epoll_create1 that Miri does not support.
|
||||
run: |
|
||||
cargo miri test --features full --lib --no-fail-fast
|
||||
cargo miri nextest run --features full --lib --no-fail-fast
|
||||
working-directory: tokio
|
||||
env:
|
||||
MIRIFLAGS: -Zmiri-disable-isolation -Zmiri-strict-provenance -Zmiri-retag-fields
|
||||
|
||||
miri-test:
|
||||
name: miri-test
|
||||
needs: basics
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install Rust ${{ env.rust_miri_nightly }}
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: ${{ env.rust_miri_nightly }}
|
||||
components: miri
|
||||
- name: Install cargo-nextest
|
||||
uses: taiki-e/install-action@v2
|
||||
with:
|
||||
tool: cargo-nextest
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: miri
|
||||
run: |
|
||||
cargo miri nextest run --features full --test '*' --no-fail-fast
|
||||
working-directory: tokio
|
||||
env:
|
||||
MIRIFLAGS: -Zmiri-disable-isolation -Zmiri-strict-provenance -Zmiri-retag-fields
|
||||
|
||||
miri-doc:
|
||||
name: miri-doc
|
||||
needs: basics
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install Rust ${{ env.rust_miri_nightly }}
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: ${{ env.rust_miri_nightly }}
|
||||
components: miri
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: miri-doc-test
|
||||
run: |
|
||||
cargo miri test --doc --all-features --no-fail-fast
|
||||
working-directory: tokio
|
||||
env:
|
||||
MIRIFLAGS: -Zmiri-disable-isolation -Zmiri-strict-provenance -Zmiri-retag-fields
|
||||
@@ -373,7 +485,7 @@ jobs:
|
||||
needs: basics
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install llvm
|
||||
# Required to resolve symbols in sanitizer output
|
||||
run: sudo apt-get install -y llvm
|
||||
@@ -395,11 +507,19 @@ jobs:
|
||||
needs: basics
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Check semver
|
||||
- uses: actions/checkout@v4
|
||||
- name: Check `tokio` semver
|
||||
uses: obi1kenobi/cargo-semver-checks-action@v2
|
||||
with:
|
||||
rust-toolchain: ${{ env.rust_stable }}
|
||||
package: tokio
|
||||
release-type: minor
|
||||
- name: Check semver for rest of the workspace
|
||||
if: ${{ !startsWith(github.event.pull_request.base.ref, 'tokio-1.') }}
|
||||
uses: obi1kenobi/cargo-semver-checks-action@v2
|
||||
with:
|
||||
rust-toolchain: ${{ env.rust_stable }}
|
||||
exclude: tokio
|
||||
release-type: minor
|
||||
|
||||
cross-check:
|
||||
@@ -412,9 +532,8 @@ jobs:
|
||||
- powerpc-unknown-linux-gnu
|
||||
- powerpc64-unknown-linux-gnu
|
||||
- arm-linux-androideabi
|
||||
- mipsel-unknown-linux-musl
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install Rust ${{ env.rust_stable }}
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
@@ -426,20 +545,52 @@ jobs:
|
||||
env:
|
||||
RUSTFLAGS: --cfg tokio_unstable -Dwarnings
|
||||
|
||||
cross-test-with-parking_lot:
|
||||
cross-check-tier3:
|
||||
name: cross-check-tier3
|
||||
needs: basics
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
target:
|
||||
- name: x86_64-unknown-haiku
|
||||
- name: armv7-sony-vita-newlibeabihf
|
||||
exclude_features: "process,signal,rt-process-signal,full"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install Rust ${{ env.rust_nightly }}
|
||||
uses: dtolnay/rust-toolchain@nightly
|
||||
with:
|
||||
toolchain: ${{ env.rust_nightly }}
|
||||
components: rust-src
|
||||
- name: Install cargo-hack
|
||||
uses: taiki-e/install-action@v2
|
||||
with:
|
||||
tool: cargo-hack
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- run: cargo hack check -Zbuild-std --workspace --each-feature --target ${{ matrix.target.name }} --exclude-features "${{ matrix.target.exclude_features }}"
|
||||
env:
|
||||
RUSTFLAGS: --cfg tokio_unstable -Dwarnings
|
||||
|
||||
cross-test-with-parking_lot:
|
||||
needs: basics
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- target: i686-unknown-linux-gnu
|
||||
os: ubuntu-latest
|
||||
rustflags: --cfg tokio_taskdump
|
||||
- target: armv5te-unknown-linux-gnueabi
|
||||
os: ubuntu-latest
|
||||
- target: armv7-unknown-linux-gnueabihf
|
||||
os: ubuntu-24.04-arm
|
||||
- target: aarch64-unknown-linux-gnu
|
||||
os: ubuntu-24.04-arm
|
||||
rustflags: --cfg tokio_taskdump
|
||||
- target: aarch64-pc-windows-msvc
|
||||
os: windows-11-arm
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install Rust stable
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
@@ -454,6 +605,7 @@ jobs:
|
||||
- uses: taiki-e/setup-cross-toolchain-action@v1
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
qemu: '7.2'
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: Tests run with all features (including parking_lot)
|
||||
@@ -467,18 +619,24 @@ jobs:
|
||||
|
||||
cross-test-without-parking_lot:
|
||||
needs: basics
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- target: i686-unknown-linux-gnu
|
||||
os: ubuntu-latest
|
||||
rustflags: --cfg tokio_taskdump
|
||||
- target: armv5te-unknown-linux-gnueabi
|
||||
os: ubuntu-latest
|
||||
- target: armv7-unknown-linux-gnueabihf
|
||||
os: ubuntu-24.04-arm
|
||||
- target: aarch64-unknown-linux-gnu
|
||||
os: ubuntu-24.04-arm
|
||||
rustflags: --cfg tokio_taskdump
|
||||
- target: aarch64-pc-windows-msvc
|
||||
os: windows-11-arm
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install Rust stable
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
@@ -493,6 +651,7 @@ jobs:
|
||||
- uses: taiki-e/setup-cross-toolchain-action@v1
|
||||
with:
|
||||
target: ${{ matrix.target }}
|
||||
qemu: '7.2'
|
||||
|
||||
- name: Remove `parking_lot` from `full` feature
|
||||
run: sed -i '0,/parking_lot/{/parking_lot/d;}' tokio/Cargo.toml
|
||||
@@ -514,7 +673,7 @@ jobs:
|
||||
needs: basics
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install Rust ${{ env.rust_nightly }}
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
@@ -529,6 +688,7 @@ jobs:
|
||||
- uses: taiki-e/setup-cross-toolchain-action@v1
|
||||
with:
|
||||
target: i686-unknown-linux-gnu
|
||||
qemu: '7.2'
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: test tokio --all-features
|
||||
@@ -544,7 +704,7 @@ jobs:
|
||||
needs: basics
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install Rust ${{ env.rust_nightly }}
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
@@ -576,8 +736,10 @@ jobs:
|
||||
- { name: "--unstable", rustflags: "--cfg tokio_unstable -Dwarnings" }
|
||||
# Try with unstable and taskdump feature flags
|
||||
- { name: "--unstable --taskdump", rustflags: "--cfg tokio_unstable -Dwarnings --cfg tokio_taskdump" }
|
||||
- { name: "--tokio_uring", rustflags: "-Dwarnings --cfg tokio_uring" }
|
||||
- { name: "--unstable --taskdump --tokio_uring", rustflags: "--cfg tokio_unstable -Dwarnings --cfg tokio_taskdump --cfg tokio_uring" }
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install Rust ${{ env.rust_nightly }}
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
@@ -596,14 +758,21 @@ jobs:
|
||||
name: minrust
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install Rust ${{ env.rust_min }}
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: ${{ env.rust_min }}
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: "check --workspace --all-features"
|
||||
run: cargo check --workspace --all-features
|
||||
run: |
|
||||
if [[ "${{ github.event.pull_request.base.ref }}" =~ ^tokio-1\..* ]]; then
|
||||
# Only check `tokio` crate as the PR is backporting to an earlier tokio release.
|
||||
cargo check -p tokio --all-features
|
||||
else
|
||||
# Check all crates in the workspace
|
||||
cargo check --workspace --all-features
|
||||
fi
|
||||
env:
|
||||
RUSTFLAGS: "" # remove -Dwarnings
|
||||
|
||||
@@ -612,7 +781,7 @@ jobs:
|
||||
needs: basics
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install Rust ${{ env.rust_nightly }}
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
@@ -631,7 +800,7 @@ jobs:
|
||||
cargo hack check --all-features --ignore-private
|
||||
- name: "check --all-features --unstable -Z minimal-versions"
|
||||
env:
|
||||
RUSTFLAGS: --cfg tokio_unstable --cfg tokio_taskdump -Dwarnings
|
||||
RUSTFLAGS: --cfg tokio_unstable --cfg tokio_taskdump --cfg tokio_uring -Dwarnings
|
||||
run: |
|
||||
# Remove dev-dependencies from Cargo.toml to prevent the next `cargo update`
|
||||
# from determining minimal versions based on dev-dependencies.
|
||||
@@ -644,7 +813,7 @@ jobs:
|
||||
name: fmt
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install Rust ${{ env.rust_stable }}
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
@@ -664,7 +833,7 @@ jobs:
|
||||
name: clippy
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install Rust ${{ env.rust_clippy }}
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
@@ -677,9 +846,17 @@ jobs:
|
||||
|
||||
docs:
|
||||
name: docs
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ${{ matrix.run.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
run:
|
||||
- os: windows-latest
|
||||
- os: ubuntu-latest
|
||||
RUSTFLAGS: --cfg tokio_taskdump --cfg tokio_uring
|
||||
RUSTDOCFLAGS: --cfg tokio_taskdump --cfg tokio_uring
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install Rust ${{ env.rust_nightly }}
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
@@ -689,15 +866,15 @@ jobs:
|
||||
run: |
|
||||
cargo doc --lib --no-deps --all-features --document-private-items
|
||||
env:
|
||||
RUSTFLAGS: --cfg docsrs --cfg tokio_unstable --cfg tokio_taskdump
|
||||
RUSTDOCFLAGS: --cfg docsrs --cfg tokio_unstable --cfg tokio_taskdump -Dwarnings
|
||||
RUSTFLAGS: --cfg docsrs --cfg tokio_unstable ${{ matrix.run.RUSTFLAGS }}
|
||||
RUSTDOCFLAGS: --cfg docsrs --cfg tokio_unstable -Dwarnings ${{ matrix.run.RUSTDOCFLAGS }}
|
||||
|
||||
loom-compile:
|
||||
name: build loom tests
|
||||
needs: basics
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install Rust ${{ env.rust_stable }}
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
@@ -713,7 +890,7 @@ jobs:
|
||||
name: Check README
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Verify that both READMEs are identical
|
||||
run: diff README.md tokio/README.md
|
||||
|
||||
@@ -732,7 +909,7 @@ jobs:
|
||||
- ubuntu-latest
|
||||
- macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install Rust ${{ env.rust_stable }}
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
@@ -773,12 +950,62 @@ jobs:
|
||||
run: cargo test --features full
|
||||
working-directory: hyper
|
||||
|
||||
test-quinn:
|
||||
name: Test Quinn
|
||||
needs: basics
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os:
|
||||
- windows-latest
|
||||
- ubuntu-latest
|
||||
- macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install Rust ${{ env.rust_stable }}
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: ${{ env.rust_stable }}
|
||||
|
||||
- name: Clone Quinn
|
||||
run: git clone https://github.com/quinn-rs/quinn.git
|
||||
|
||||
- name: Checkout the latest release because HEAD maybe contains breakage.
|
||||
run: |
|
||||
set -x
|
||||
tag=$(git describe --abbrev=0 --tags)
|
||||
git checkout "${tag}"
|
||||
working-directory: quinn
|
||||
|
||||
- name: Patch Quinn to use tokio from this repository
|
||||
run: |
|
||||
set -x
|
||||
echo '[patch.crates-io]' >>Cargo.toml
|
||||
echo 'tokio = { path = "../tokio" }' >>Cargo.toml
|
||||
git diff
|
||||
working-directory: quinn
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
# The cargo workspaces and target directory configuration.
|
||||
# These entries are separated by newlines and have the form
|
||||
# `$workspace -> $target`. The `$target` part is treated as a directory
|
||||
# relative to the `$workspace` and defaults to "target" if not explicitly given.
|
||||
# default: ". -> target"
|
||||
workspaces: "./quinn"
|
||||
|
||||
- name: Test Quinn
|
||||
working-directory: quinn
|
||||
env:
|
||||
RUSTFLAGS: ""
|
||||
run: cargo test
|
||||
|
||||
x86_64-fortanix-unknown-sgx:
|
||||
name: build tokio for x86_64-fortanix-unknown-sgx
|
||||
needs: basics
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install Rust ${{ env.rust_nightly }}
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
@@ -795,7 +1022,7 @@ jobs:
|
||||
needs: basics
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install Rust ${{ env.rust_nightly }}
|
||||
uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
@@ -810,11 +1037,11 @@ jobs:
|
||||
needs: basics
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install Rust ${{ env.rust_stable }}
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install Rust 1.88.0
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: ${{ env.rust_stable }}
|
||||
toolchain: 1.88.0
|
||||
- name: Install wasm-pack
|
||||
uses: taiki-e/install-action@wasm-pack
|
||||
|
||||
@@ -823,47 +1050,67 @@ jobs:
|
||||
run: wasm-pack test --node -- --features "macros sync"
|
||||
working-directory: tokio
|
||||
|
||||
wasm32-wasi:
|
||||
name: wasm32-wasi
|
||||
wasm32-wasip1:
|
||||
name: ${{ matrix.target }}
|
||||
needs: basics
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
target:
|
||||
- wasm32-wasip1
|
||||
- wasm32-wasip1-threads
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install Rust ${{ env.rust_stable }}
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install Rust 1.88.0
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: ${{ env.rust_stable }}
|
||||
targets: wasm32-wasi
|
||||
toolchain: 1.88.0
|
||||
targets: ${{ matrix.target }}
|
||||
|
||||
# Install dependencies
|
||||
- name: Install cargo-hack, wasmtime, and cargo-wasi
|
||||
- name: Install cargo-hack, wasmtime
|
||||
uses: taiki-e/install-action@v2
|
||||
with:
|
||||
tool: cargo-hack,wasmtime,cargo-wasi
|
||||
tool: cargo-hack,wasmtime
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: WASI test tokio full
|
||||
run: cargo test -p tokio --target wasm32-wasi --features full
|
||||
run: cargo test -p tokio --target ${{ matrix.target }} --features full
|
||||
env:
|
||||
CARGO_TARGET_WASM32_WASI_RUNNER: "wasmtime run --"
|
||||
RUSTFLAGS: --cfg tokio_unstable -Dwarnings
|
||||
CARGO_TARGET_WASM32_WASIP1_RUNNER: "wasmtime run --"
|
||||
CARGO_TARGET_WASM32_WASIP1_THREADS_RUNNER: "wasmtime run -W bulk-memory=y -W threads=y -W shared-memory=y -S threads=y --"
|
||||
RUSTFLAGS: --cfg tokio_unstable -Dwarnings -C target-feature=+atomics,+bulk-memory -C link-args=--max-memory=67108864
|
||||
|
||||
- name: WASI test tokio-util full
|
||||
run: cargo test -p tokio-util --target wasm32-wasi --features full
|
||||
run: cargo test -p tokio-util --target ${{ matrix.target }} --features full
|
||||
env:
|
||||
CARGO_TARGET_WASM32_WASI_RUNNER: "wasmtime run --"
|
||||
RUSTFLAGS: --cfg tokio_unstable -Dwarnings
|
||||
CARGO_TARGET_WASM32_WASIP1_RUNNER: "wasmtime run --"
|
||||
CARGO_TARGET_WASM32_WASIP1_THREADS_RUNNER: "wasmtime run -W bulk-memory=y -W threads=y -W shared-memory=y -S threads=y --"
|
||||
RUSTFLAGS: --cfg tokio_unstable -Dwarnings -C target-feature=+atomics,+bulk-memory -C link-args=--max-memory=67108864
|
||||
|
||||
- name: WASI test tokio-stream
|
||||
run: cargo test -p tokio-stream --target wasm32-wasi --features time,net,io-util,sync
|
||||
run: cargo test -p tokio-stream --target ${{ matrix.target }} --features time,net,io-util,sync
|
||||
env:
|
||||
CARGO_TARGET_WASM32_WASI_RUNNER: "wasmtime run --"
|
||||
RUSTFLAGS: --cfg tokio_unstable -Dwarnings
|
||||
CARGO_TARGET_WASM32_WASIP1_RUNNER: "wasmtime run --"
|
||||
CARGO_TARGET_WASM32_WASIP1_THREADS_RUNNER: "wasmtime run -W bulk-memory=y -W threads=y -W shared-memory=y -S threads=y --"
|
||||
RUSTFLAGS: --cfg tokio_unstable -Dwarnings -C target-feature=+atomics,+bulk-memory -C link-args=--max-memory=67108864
|
||||
|
||||
- name: test tests-integration --features wasi-rt
|
||||
# TODO: this should become: `cargo hack wasi test --each-feature`
|
||||
run: cargo wasi test --test rt_yield --features wasi-rt
|
||||
run: cargo test --target ${{ matrix.target }} --test rt_yield --features wasi-rt
|
||||
if: matrix.target == 'wasm32-wasip1'
|
||||
working-directory: tests-integration
|
||||
env:
|
||||
CARGO_TARGET_WASM32_WASIP1_RUNNER: "wasmtime run --"
|
||||
RUSTFLAGS: -Dwarnings -C target-feature=+atomics,+bulk-memory -C link-args=--max-memory=67108864
|
||||
|
||||
- name: test tests-integration --features wasi-threads-rt
|
||||
run: cargo test --target ${{ matrix.target }} --features wasi-threads-rt
|
||||
if: matrix.target == 'wasm32-wasip1-threads'
|
||||
working-directory: tests-integration
|
||||
env:
|
||||
CARGO_TARGET_WASM32_WASIP1_THREADS_RUNNER: "wasmtime run -W bulk-memory=y -W threads=y -W shared-memory=y -S threads=y --"
|
||||
RUSTFLAGS: --cfg tokio_unstable -Dwarnings -C target-feature=+atomics,+bulk-memory -C link-args=--max-memory=67108864
|
||||
|
||||
check-external-types:
|
||||
name: check-external-types (${{ matrix.os }})
|
||||
@@ -877,9 +1124,9 @@ jobs:
|
||||
rust:
|
||||
# `check-external-types` requires a specific Rust nightly version. See
|
||||
# the README for details: https://github.com/awslabs/cargo-check-external-types
|
||||
- nightly-2023-05-31
|
||||
- nightly-2024-06-30
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install Rust ${{ matrix.rust }}
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
@@ -888,9 +1135,9 @@ jobs:
|
||||
- name: Install cargo-check-external-types
|
||||
uses: taiki-e/cache-cargo-install-action@v1
|
||||
with:
|
||||
tool: [email protected].7
|
||||
tool: [email protected].13
|
||||
- name: check-external-types
|
||||
run: cargo check-external-types --all-features --config external-types.toml
|
||||
run: cargo check-external-types --all-features
|
||||
working-directory: tokio
|
||||
|
||||
check-fuzzing:
|
||||
@@ -898,7 +1145,7 @@ jobs:
|
||||
needs: basics
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install Rust ${{ env.rust_nightly }}
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
@@ -912,3 +1159,75 @@ jobs:
|
||||
- name: Check /tokio-stream/
|
||||
run: cargo fuzz check --all-features
|
||||
working-directory: tokio-stream
|
||||
|
||||
check-spelling:
|
||||
name: check-spelling
|
||||
needs: basics
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install Rust ${{ env.rust_stable }}
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: ${{ env.rust_stable }}
|
||||
- name: Install cargo-spellcheck
|
||||
uses: taiki-e/install-action@v2
|
||||
with:
|
||||
tool: cargo-spellcheck
|
||||
- uses: actions/checkout@v4
|
||||
- name: Make sure dictionary words are sorted and unique
|
||||
run: |
|
||||
FILE="spellcheck.dic"
|
||||
|
||||
# Verify the first line is an integer.
|
||||
first_line=$(head -n 1 "$FILE")
|
||||
if ! [[ "$first_line" =~ ^[0-9]+$ ]]; then
|
||||
echo "Error: The first line of $FILE must be an integer, but got: '$first_line'"
|
||||
exit 1
|
||||
fi
|
||||
expected_count="$first_line"
|
||||
|
||||
# Check that the number of lines matches the integer.
|
||||
# xargs (with no arguments) will strip leading/trailing whitespacefrom wc's output.
|
||||
actual_count=$(sed '1d' "$FILE" | wc -l | xargs)
|
||||
if [ "$expected_count" -ne "$actual_count" ]; then
|
||||
echo "Error: The number of lines ($actual_count) does not match $expected_count."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# `sed` removes the first line (number of words).
|
||||
#
|
||||
# `sort` makes sure everything in between is sorted
|
||||
# and contains no duplicates.
|
||||
#
|
||||
# Since `sort` is sensitive to locale, we set it
|
||||
# using LC_ALL to en_US.UTF8 to be consistent in different
|
||||
# environments.
|
||||
|
||||
(
|
||||
sed '1d' $FILE | LC_ALL=en_US.UTF8 sort -uc
|
||||
) || {
|
||||
echo "Dictionary is not in sorted order. Correct order is:"
|
||||
LC_ALL=en_US.UTF8 sort -u <(sed '1d' $FILE)
|
||||
false
|
||||
}
|
||||
- name: Run cargo-spellcheck
|
||||
run: |
|
||||
if ! cargo spellcheck --code 1
|
||||
then
|
||||
echo ''
|
||||
echo ''
|
||||
echo 'If this is a Rust method/type/variable name, then you should'
|
||||
echo 'enclose it in backticks like this: `MyRustType`.'
|
||||
echo ''
|
||||
echo 'If this is a real word, then you can add it to spellcheck.dic'
|
||||
exit 1
|
||||
fi
|
||||
- name: Detect trailing whitespace
|
||||
run: |
|
||||
if grep --exclude-dir=.git --exclude-dir=target -rne '\s$' .
|
||||
then
|
||||
echo ''
|
||||
echo 'Please remove trailing whitespace from these lines.'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -29,7 +29,7 @@ jobs:
|
||||
if: github.repository_owner == 'tokio-rs' && (contains(github.event.pull_request.labels.*.name, 'R-loom-sync') || (github.base_ref == null))
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install Rust ${{ env.rust_stable }}
|
||||
uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
@@ -45,7 +45,7 @@ jobs:
|
||||
if: github.repository_owner == 'tokio-rs' && (contains(github.event.pull_request.labels.*.name, 'R-loom-time-driver') || (github.base_ref == null))
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install Rust ${{ env.rust_stable }}
|
||||
uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
@@ -61,7 +61,7 @@ jobs:
|
||||
if: github.repository_owner == 'tokio-rs' && (contains(github.event.pull_request.labels.*.name, 'R-loom-current-thread') || (github.base_ref == null))
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install Rust ${{ env.rust_stable }}
|
||||
uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
@@ -84,39 +84,14 @@ jobs:
|
||||
- scope: loom_multi_thread::group_c
|
||||
- scope: loom_multi_thread::group_d
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install Rust ${{ env.rust_stable }}
|
||||
uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: ${{ env.rust_stable }}
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: loom ${{ matrix.scope }}
|
||||
run: cargo test --lib --release --features full -- $SCOPE
|
||||
working-directory: tokio
|
||||
env:
|
||||
SCOPE: ${{ matrix.scope }}
|
||||
|
||||
loom-multi-thread-alt:
|
||||
name: loom ALT multi-thread scheduler
|
||||
# base_ref is null when it's not a pull request
|
||||
if: github.repository_owner == 'tokio-rs' && (contains(github.event.pull_request.labels.*.name, 'R-loom-multi-thread-alt') || (github.base_ref == null))
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- scope: loom_multi_thread_alt::group_a
|
||||
- scope: loom_multi_thread_alt::group_b
|
||||
- scope: loom_multi_thread_alt::group_c
|
||||
- scope: loom_multi_thread_alt::group_d
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install Rust ${{ env.rust_stable }}
|
||||
uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: ${{ env.rust_stable }}
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- name: loom ${{ matrix.scope }}
|
||||
run: cargo test --lib --release --features full -- $SCOPE
|
||||
run: cargo test --lib --release --features full -- --nocapture $SCOPE
|
||||
working-directory: tokio
|
||||
env:
|
||||
SCOPE: ${{ matrix.scope }}
|
||||
|
||||
@@ -16,17 +16,8 @@ permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
security-audit:
|
||||
cargo-deny:
|
||||
runs-on: ubuntu-latest
|
||||
if: "!contains(github.event.head_commit.message, 'ci skip')"
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Install cargo-audit
|
||||
run: cargo install cargo-audit
|
||||
|
||||
- name: Generate lockfile
|
||||
run: cargo generate-lockfile
|
||||
|
||||
- name: Audit dependencies
|
||||
run: cargo audit
|
||||
- uses: actions/checkout@v4
|
||||
- uses: EmbarkStudios/cargo-deny-action@v2
|
||||
|
||||
@@ -13,7 +13,7 @@ env:
|
||||
RUSTFLAGS: -Dwarnings
|
||||
RUST_BACKTRACE: 1
|
||||
# Change to specific Rust release to pin
|
||||
rust_stable: stable
|
||||
rust_stable: 1.82
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -27,7 +27,7 @@ jobs:
|
||||
stress-test:
|
||||
- simple_echo_tcp
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install Rust ${{ env.rust_stable }}
|
||||
uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
|
||||
+73
-20
@@ -149,23 +149,31 @@ When updating this, also update:
|
||||
-->
|
||||
|
||||
```
|
||||
cargo +1.65.0 clippy --all --tests --all-features
|
||||
cargo +1.88 clippy --all --tests --all-features
|
||||
```
|
||||
|
||||
When building documentation normally, the markers that list the features
|
||||
required for various parts of Tokio are missing. To build the documentation
|
||||
correctly, use this command:
|
||||
When building documentation, a simple `cargo doc` is not sufficient. To produce
|
||||
documentation equivalent to what will be produced in docs.rs's builds of Tokio's
|
||||
docs, please use:
|
||||
|
||||
```
|
||||
RUSTDOCFLAGS="--cfg docsrs" RUSTFLAGS="--cfg docsrs" cargo +nightly doc --all-features
|
||||
RUSTDOCFLAGS="--cfg docsrs --cfg tokio_unstable" RUSTFLAGS="--cfg docsrs --cfg tokio_unstable" cargo +nightly doc --all-features [--open]
|
||||
```
|
||||
|
||||
To build documentation including Tokio's unstable features, it is necessary to
|
||||
pass `--cfg tokio_unstable` to both RustDoc *and* rustc. To build the
|
||||
documentation for unstable features, use this command:
|
||||
This turns on indicators to display the Cargo features required for
|
||||
conditionally compiled APIs in Tokio, and it enables documentation of unstable
|
||||
Tokio features. Notice that it is necessary to pass cfg flags to both RustDoc
|
||||
*and* rustc.
|
||||
|
||||
There is a more concise way to build docs.rs-equivalent docs by using [`cargo
|
||||
docs-rs`], which reads the above documentation flags out of Tokio's Cargo.toml
|
||||
as docs.rs itself does.
|
||||
|
||||
[`cargo docs-rs`]: https://github.com/dtolnay/cargo-docs-rs
|
||||
|
||||
```
|
||||
RUSTDOCFLAGS="--cfg docsrs --cfg tokio_unstable" RUSTFLAGS="--cfg docsrs --cfg tokio_unstable" cargo +nightly doc --all-features
|
||||
cargo install --locked cargo-docs-rs
|
||||
cargo +nightly docs-rs [--open]
|
||||
```
|
||||
|
||||
The `cargo fmt` command does not work on the Tokio codebase. You can use the
|
||||
@@ -184,16 +192,36 @@ it, `rustfmt` will update your files locally instead.
|
||||
You can run loom tests with
|
||||
```
|
||||
cd tokio # tokio crate in workspace
|
||||
LOOM_MAX_PREEMPTIONS=1 RUSTFLAGS="--cfg loom" \
|
||||
LOOM_MAX_PREEMPTIONS=1 LOOM_MAX_BRANCHES=10000 RUSTFLAGS="--cfg loom -C debug_assertions" \
|
||||
cargo test --lib --release --features full -- --test-threads=1 --nocapture
|
||||
```
|
||||
Additionally, you can also add `--cfg tokio_unstable` to the `RUSTFLAGS` environment variable to
|
||||
run loom tests that test unstable features.
|
||||
|
||||
You can run miri tests with
|
||||
```
|
||||
MIRIFLAGS="-Zmiri-disable-isolation -Zmiri-tag-raw-pointers" \
|
||||
cargo +nightly miri test --features full --lib
|
||||
MIRIFLAGS="-Zmiri-disable-isolation -Zmiri-strict-provenance -Zmiri-retag-fields" \
|
||||
cargo +nightly miri test --features full --lib --tests
|
||||
```
|
||||
|
||||
### Performing spellcheck on tokio codebase
|
||||
|
||||
You can perform spell-check on tokio codebase. For details of how to use the spellcheck tool, feel free to visit
|
||||
https://github.com/drahnr/cargo-spellcheck
|
||||
```
|
||||
# First install the spell-check plugin
|
||||
cargo install --locked cargo-spellcheck
|
||||
|
||||
# Then run the cargo spell check command
|
||||
cargo spellcheck check
|
||||
```
|
||||
|
||||
if the command rejects a word, you should backtick the rejected word if it's code related. If not, the
|
||||
rejected word should be put into `spellcheck.dic` file.
|
||||
|
||||
Note that when you add a word into the file, you should also update the first line which tells the spellcheck tool
|
||||
the total number of words included in the file
|
||||
|
||||
### Tests
|
||||
|
||||
If the change being proposed alters code (as opposed to only documentation for
|
||||
@@ -233,7 +261,7 @@ directory `fuzz`. It is a good idea to run fuzz tests after each change.
|
||||
To get started with fuzz testing you'll need to install
|
||||
[cargo-fuzz](https://github.com/rust-fuzz/cargo-fuzz).
|
||||
|
||||
`cargo install cargo-fuzz`
|
||||
`cargo install --locked cargo-fuzz`
|
||||
|
||||
To list the available fuzzing harnesses you can run;
|
||||
|
||||
@@ -241,7 +269,7 @@ To list the available fuzzing harnesses you can run;
|
||||
$ cd tokio
|
||||
$ cargo fuzz list
|
||||
fuzz_linked_list
|
||||
````
|
||||
```
|
||||
|
||||
Running a fuzz test is as simple as;
|
||||
|
||||
@@ -327,6 +355,29 @@ example would explicitly use `Timeout::new`. For example:
|
||||
/// # }
|
||||
```
|
||||
|
||||
### Benchmarks
|
||||
|
||||
You can run benchmarks locally for the changes you've made to the tokio codebase.
|
||||
Tokio currently uses [Criterion](https://github.com/bheisler/criterion.rs) as its benchmarking tool. To run a benchmark
|
||||
against the changes you have made, for example, you can run;
|
||||
|
||||
```bash
|
||||
cd benches
|
||||
|
||||
# Run all benchmarks.
|
||||
cargo bench
|
||||
|
||||
# Run all tests in the `benches/fs.rs` file
|
||||
cargo bench --bench fs
|
||||
|
||||
# Run the `async_read_buf` benchmark in `benches/fs.rs` specifically.
|
||||
cargo bench async_read_buf
|
||||
|
||||
# After running benches, you can check the statistics under `tokio/target/criterion/`
|
||||
```
|
||||
|
||||
You can also refer to Criterion docs for additional options and details.
|
||||
|
||||
### Commits
|
||||
|
||||
It is a recommended best practice to keep your changes as logically grouped as
|
||||
@@ -350,14 +401,16 @@ A good commit message should describe what changed and why.
|
||||
and no more than 72 characters)
|
||||
* be entirely in lowercase with the exception of proper nouns, acronyms, and
|
||||
the words that refer to code, like function/variable names
|
||||
* be prefixed with the name of the sub crate being changed (without the `tokio-`
|
||||
prefix) and start with an imperative verb. If modifying `tokio` proper,
|
||||
omit the crate prefix.
|
||||
* start with an imperative verb
|
||||
* not have a period at the end
|
||||
* be prefixed with the name of the module being changed; usually this is the
|
||||
same as the M-* label on the PR
|
||||
|
||||
Examples:
|
||||
|
||||
* timer: introduce `Timeout` and deprecate `Deadline`
|
||||
* export `Encoder`, `Decoder`, `Framed*` from tokio_codec
|
||||
* time: introduce `Timeout` and deprecate `Deadline`
|
||||
* codec: export `Encoder`, `Decoder`, `Framed*`
|
||||
* ci: fix the FreeBSD ci configuration
|
||||
|
||||
2. Keep the second line blank.
|
||||
3. Wrap all other lines at 72 columns (except for long URLs).
|
||||
@@ -374,7 +427,7 @@ A good commit message should describe what changed and why.
|
||||
Sample complete commit message:
|
||||
|
||||
```txt
|
||||
subcrate: explain the commit in one line
|
||||
module: explain the commit in one line
|
||||
|
||||
Body of commit message is a few lines of text, explaining things
|
||||
in more detail, possibly giving some background about the issue
|
||||
|
||||
Generated
+2100
File diff suppressed because it is too large
Load Diff
+18
@@ -14,3 +14,21 @@ members = [
|
||||
"tests-build",
|
||||
"tests-integration",
|
||||
]
|
||||
|
||||
[workspace.metadata.spellcheck]
|
||||
config = "spellcheck.toml"
|
||||
|
||||
[workspace.lints.rust]
|
||||
unexpected_cfgs = { level = "warn", check-cfg = [
|
||||
'cfg(fuzzing)',
|
||||
'cfg(loom)',
|
||||
'cfg(mio_unsupported_force_poll_poll)',
|
||||
'cfg(tokio_allow_from_blocking_fd)',
|
||||
'cfg(tokio_internal_mt_counters)',
|
||||
'cfg(tokio_no_parking_lot)',
|
||||
'cfg(tokio_no_tuning_tests)',
|
||||
'cfg(tokio_taskdump)',
|
||||
'cfg(tokio_unstable)',
|
||||
'cfg(tokio_uring)',
|
||||
'cfg(target_os, values("cygwin"))',
|
||||
] }
|
||||
|
||||
@@ -1,25 +1,21 @@
|
||||
Copyright (c) 2023 Tokio Contributors
|
||||
MIT License
|
||||
|
||||
Permission is hereby granted, free of charge, to any
|
||||
person obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the
|
||||
Software without restriction, including without
|
||||
limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software
|
||||
is furnished to do so, subject to the following
|
||||
conditions:
|
||||
Copyright (c) Tokio Contributors
|
||||
|
||||
The above copyright notice and this permission notice
|
||||
shall be included in all copies or substantial portions
|
||||
of the Software.
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
@@ -39,7 +39,7 @@ level, it provides a few major components:
|
||||
|
||||
* A multithreaded, work-stealing based task [scheduler].
|
||||
* A reactor backed by the operating system's event queue (epoll, kqueue,
|
||||
IOCP, etc...).
|
||||
IOCP, etc.).
|
||||
* Asynchronous [TCP and UDP][net] sockets.
|
||||
|
||||
These components provide the runtime components necessary for building
|
||||
@@ -56,7 +56,7 @@ Make sure you activated the full features of the tokio crate on Cargo.toml:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
tokio = { version = "1.32.1", features = ["full"] }
|
||||
tokio = { version = "1.47.3", features = ["full"] }
|
||||
```
|
||||
Then, on your main.rs:
|
||||
|
||||
@@ -78,7 +78,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
loop {
|
||||
let n = match socket.read(&mut buf).await {
|
||||
// socket closed
|
||||
Ok(n) if n == 0 => return,
|
||||
Ok(0) => return,
|
||||
Ok(n) => n,
|
||||
Err(e) => {
|
||||
eprintln!("failed to read from socket; err = {:?}", e);
|
||||
@@ -132,6 +132,8 @@ project.
|
||||
In addition to the crates in this repository, the Tokio project also maintains
|
||||
several other libraries, including:
|
||||
|
||||
* [`axum`]: A web application framework that focuses on ergonomics and modularity.
|
||||
|
||||
* [`hyper`]: A fast and correct HTTP/1.1 and HTTP/2 implementation for Rust.
|
||||
|
||||
* [`tonic`]: A gRPC over HTTP/2 implementation focused on high performance, interoperability, and flexibility.
|
||||
@@ -142,21 +144,18 @@ several other libraries, including:
|
||||
|
||||
* [`tracing`] (formerly `tokio-trace`): A framework for application-level tracing and async-aware diagnostics.
|
||||
|
||||
* [`rdbc`]: A Rust database connectivity library for MySQL, Postgres and SQLite.
|
||||
|
||||
* [`mio`]: A low-level, cross-platform abstraction over OS I/O APIs that powers
|
||||
`tokio`.
|
||||
* [`mio`]: A low-level, cross-platform abstraction over OS I/O APIs that powers `tokio`.
|
||||
|
||||
* [`bytes`]: Utilities for working with bytes, including efficient byte buffers.
|
||||
|
||||
* [`loom`]: A testing tool for concurrent Rust code
|
||||
* [`loom`]: A testing tool for concurrent Rust code.
|
||||
|
||||
[`axum`]: https://github.com/tokio-rs/axum
|
||||
[`warp`]: https://github.com/seanmonstar/warp
|
||||
[`hyper`]: https://github.com/hyperium/hyper
|
||||
[`tonic`]: https://github.com/hyperium/tonic
|
||||
[`tower`]: https://github.com/tower-rs/tower
|
||||
[`loom`]: https://github.com/tokio-rs/loom
|
||||
[`rdbc`]: https://github.com/tokio-rs/rdbc
|
||||
[`tracing`]: https://github.com/tokio-rs/tracing
|
||||
[`mio`]: https://github.com/tokio-rs/mio
|
||||
[`bytes`]: https://github.com/tokio-rs/bytes
|
||||
@@ -187,12 +186,13 @@ When updating this, also update:
|
||||
|
||||
Tokio will keep a rolling MSRV (minimum supported rust version) policy of **at
|
||||
least** 6 months. When increasing the MSRV, the new Rust version must have been
|
||||
released at least six months ago. The current MSRV is 1.63.
|
||||
released at least six months ago. The current MSRV is 1.70.
|
||||
|
||||
Note that the MSRV is not increased automatically, and only as part of a minor
|
||||
release. The MSRV history for past minor releases can be found below:
|
||||
|
||||
* 1.30 to now - Rust 1.63
|
||||
* 1.39 to now - Rust 1.70
|
||||
* 1.30 to 1.38 - Rust 1.63
|
||||
* 1.27 to 1.29 - Rust 1.56
|
||||
* 1.17 to 1.26 - Rust 1.49
|
||||
* 1.15 to 1.16 - Rust 1.46
|
||||
@@ -205,8 +205,8 @@ works with the MSRV of that minor release.
|
||||
|
||||
## Release schedule
|
||||
|
||||
Tokio doesn't follow a fixed release schedule, but we typically make one to two
|
||||
new minor releases each month. We make patch releases for bugfixes as necessary.
|
||||
Tokio doesn't follow a fixed release schedule, but we typically make one minor
|
||||
release each month. We make patch releases for bugfixes as necessary.
|
||||
|
||||
## Bug patching policy
|
||||
|
||||
@@ -216,18 +216,18 @@ warrants a patch release with a fix for the bug, it will be backported and
|
||||
released as a new patch release for each LTS minor version. Our current LTS
|
||||
releases are:
|
||||
|
||||
* `1.20.x` - LTS release until September 2023. (MSRV 1.49)
|
||||
* `1.25.x` - LTS release until March 2024. (MSRV 1.49)
|
||||
* `1.43.x` - LTS release until March 2026. (MSRV 1.70)
|
||||
* `1.47.x` - LTS release until September 2026. (MSRV 1.70)
|
||||
|
||||
Each LTS release will continue to receive backported fixes for at least a year.
|
||||
If you wish to use a fixed minor release in your project, we recommend that you
|
||||
use an LTS release.
|
||||
|
||||
To use a fixed minor version, you can specify the version with a tilde. For
|
||||
example, to specify that you wish to use the newest `1.18.x` patch release, you
|
||||
example, to specify that you wish to use the newest `1.32.x` patch release, you
|
||||
can use the following dependency specification:
|
||||
```text
|
||||
tokio = { version = "~1.18", features = [...] }
|
||||
tokio = { version = "~1.43", features = [...] }
|
||||
```
|
||||
|
||||
### Previous LTS releases
|
||||
@@ -235,6 +235,11 @@ tokio = { version = "~1.18", features = [...] }
|
||||
* `1.8.x` - LTS release until February 2022.
|
||||
* `1.14.x` - LTS release until June 2022.
|
||||
* `1.18.x` - LTS release until June 2023.
|
||||
* `1.20.x` - LTS release until September 2023.
|
||||
* `1.25.x` - LTS release until March 2024.
|
||||
* `1.32.x` - LTS release until September 2024.
|
||||
* `1.36.x` - LTS release until March 2025.
|
||||
* `1.38.x` - LTS release until July 2025.
|
||||
|
||||
## License
|
||||
|
||||
|
||||
+18
-4
@@ -3,19 +3,20 @@ name = "benches"
|
||||
version = "0.0.0"
|
||||
publish = false
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
|
||||
[features]
|
||||
test-util = ["tokio/test-util"]
|
||||
|
||||
[dependencies]
|
||||
tokio = { version = "1.5.0", path = "../tokio", features = ["full"] }
|
||||
bencher = "0.1.5"
|
||||
rand = "0.8"
|
||||
rand_chacha = "0.3"
|
||||
criterion = "0.5.1"
|
||||
rand = "0.9"
|
||||
rand_chacha = "0.9"
|
||||
|
||||
[dev-dependencies]
|
||||
tokio-util = { version = "0.7.0", path = "../tokio-util", features = ["full"] }
|
||||
tokio-stream = { path = "../tokio-stream" }
|
||||
tokio-stream = { version = "0.1", path = "../tokio-stream" }
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
libc = "0.2.42"
|
||||
@@ -25,6 +26,11 @@ name = "spawn"
|
||||
path = "spawn.rs"
|
||||
harness = false
|
||||
|
||||
[[bench]]
|
||||
name = "sync_broadcast"
|
||||
path = "sync_broadcast.rs"
|
||||
harness = false
|
||||
|
||||
[[bench]]
|
||||
name = "sync_mpsc"
|
||||
path = "sync_mpsc.rs"
|
||||
@@ -84,3 +90,11 @@ harness = false
|
||||
name = "time_now"
|
||||
path = "time_now.rs"
|
||||
harness = false
|
||||
|
||||
[[bench]]
|
||||
name = "time_timeout"
|
||||
path = "time_timeout.rs"
|
||||
harness = false
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
+56
-44
@@ -1,4 +1,4 @@
|
||||
use bencher::{benchmark_group, benchmark_main, Bencher};
|
||||
use criterion::{criterion_group, criterion_main, Criterion};
|
||||
|
||||
use rand::{Rng, SeedableRng};
|
||||
use rand_chacha::ChaCha20Rng;
|
||||
@@ -29,7 +29,7 @@ const WRITE_SERVICE_PERIOD: Duration = Duration::from_millis(20);
|
||||
// because another writer claimed the buffer space
|
||||
const PROBABILITY_FLUSH_WAIT: f64 = 0.1;
|
||||
|
||||
/// A slow writer that aims to simulate HDD behaviour under heavy load.
|
||||
/// A slow writer that aims to simulate HDD behavior under heavy load.
|
||||
///
|
||||
/// There is a limited buffer, which is fully drained on the next write after
|
||||
/// a time limit is reached. Flush waits for the time limit to be reached
|
||||
@@ -64,7 +64,7 @@ impl SlowHddWriter {
|
||||
) -> std::task::Poll<Result<(), std::io::Error>> {
|
||||
// If we hit a service interval, the buffer can be cleared
|
||||
let res = self.service_intervals.poll_tick(cx).map(|_| Ok(()));
|
||||
if let Poll::Ready(_) = res {
|
||||
if res.is_ready() {
|
||||
self.buffer_used = 0;
|
||||
}
|
||||
res
|
||||
@@ -77,7 +77,7 @@ impl SlowHddWriter {
|
||||
) -> std::task::Poll<Result<usize, std::io::Error>> {
|
||||
let service_res = self.as_mut().service_write(cx);
|
||||
|
||||
if service_res.is_pending() && self.blocking_rng.gen_bool(PROBABILITY_FLUSH_WAIT) {
|
||||
if service_res.is_pending() && self.blocking_rng.random_bool(PROBABILITY_FLUSH_WAIT) {
|
||||
return Poll::Pending;
|
||||
}
|
||||
let available = self.buffer_size - self.buffer_used;
|
||||
@@ -123,7 +123,7 @@ impl AsyncWrite for SlowHddWriter {
|
||||
cx: &mut std::task::Context<'_>,
|
||||
bufs: &[std::io::IoSlice<'_>],
|
||||
) -> std::task::Poll<Result<usize, std::io::Error>> {
|
||||
let writeable = bufs.into_iter().fold(0, |acc, buf| acc + buf.len());
|
||||
let writeable = bufs.iter().fold(0, |acc, buf| acc + buf.len());
|
||||
self.write_bytes(cx, writeable)
|
||||
}
|
||||
|
||||
@@ -145,7 +145,7 @@ impl ChunkReader {
|
||||
fn new(chunk_size: usize, service_interval: Duration) -> Self {
|
||||
let mut service_intervals = interval(service_interval);
|
||||
service_intervals.set_missed_tick_behavior(MissedTickBehavior::Burst);
|
||||
let data: Vec<u8> = std::iter::repeat(0).take(chunk_size).collect();
|
||||
let data: Vec<u8> = std::iter::repeat_n(0, chunk_size).collect();
|
||||
Self {
|
||||
data,
|
||||
service_intervals,
|
||||
@@ -174,65 +174,77 @@ fn rt() -> tokio::runtime::Runtime {
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn copy_mem_to_mem(b: &mut Bencher) {
|
||||
fn copy_mem_to_mem(c: &mut Criterion) {
|
||||
let rt = rt();
|
||||
|
||||
b.iter(|| {
|
||||
let task = || async {
|
||||
let mut source = repeat(0).take(SOURCE_SIZE);
|
||||
let mut dest = Vec::new();
|
||||
copy(&mut source, &mut dest).await.unwrap();
|
||||
};
|
||||
c.bench_function("copy_mem_to_mem", |b| {
|
||||
b.iter(|| {
|
||||
let task = || async {
|
||||
let mut source = repeat(0).take(SOURCE_SIZE);
|
||||
let mut dest = Vec::new();
|
||||
copy(&mut source, &mut dest).await.unwrap();
|
||||
};
|
||||
|
||||
rt.block_on(task());
|
||||
})
|
||||
rt.block_on(task());
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn copy_mem_to_slow_hdd(b: &mut Bencher) {
|
||||
fn copy_mem_to_slow_hdd(c: &mut Criterion) {
|
||||
let rt = rt();
|
||||
|
||||
b.iter(|| {
|
||||
let task = || async {
|
||||
let mut source = repeat(0).take(SOURCE_SIZE);
|
||||
let mut dest = SlowHddWriter::new(WRITE_SERVICE_PERIOD, WRITE_BUFFER);
|
||||
copy(&mut source, &mut dest).await.unwrap();
|
||||
};
|
||||
c.bench_function("copy_mem_to_slow_hdd", |b| {
|
||||
b.iter(|| {
|
||||
let task = || async {
|
||||
let mut source = repeat(0).take(SOURCE_SIZE);
|
||||
let mut dest = SlowHddWriter::new(WRITE_SERVICE_PERIOD, WRITE_BUFFER);
|
||||
copy(&mut source, &mut dest).await.unwrap();
|
||||
};
|
||||
|
||||
rt.block_on(task());
|
||||
})
|
||||
rt.block_on(task());
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn copy_chunk_to_mem(b: &mut Bencher) {
|
||||
fn copy_chunk_to_mem(c: &mut Criterion) {
|
||||
let rt = rt();
|
||||
b.iter(|| {
|
||||
let task = || async {
|
||||
let mut source = ChunkReader::new(CHUNK_SIZE, READ_SERVICE_PERIOD).take(SOURCE_SIZE);
|
||||
let mut dest = Vec::new();
|
||||
copy(&mut source, &mut dest).await.unwrap();
|
||||
};
|
||||
|
||||
rt.block_on(task());
|
||||
})
|
||||
c.bench_function("copy_chunk_to_mem", |b| {
|
||||
b.iter(|| {
|
||||
let task = || async {
|
||||
let mut source =
|
||||
ChunkReader::new(CHUNK_SIZE, READ_SERVICE_PERIOD).take(SOURCE_SIZE);
|
||||
let mut dest = Vec::new();
|
||||
copy(&mut source, &mut dest).await.unwrap();
|
||||
};
|
||||
|
||||
rt.block_on(task());
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn copy_chunk_to_slow_hdd(b: &mut Bencher) {
|
||||
fn copy_chunk_to_slow_hdd(c: &mut Criterion) {
|
||||
let rt = rt();
|
||||
b.iter(|| {
|
||||
let task = || async {
|
||||
let mut source = ChunkReader::new(CHUNK_SIZE, READ_SERVICE_PERIOD).take(SOURCE_SIZE);
|
||||
let mut dest = SlowHddWriter::new(WRITE_SERVICE_PERIOD, WRITE_BUFFER);
|
||||
copy(&mut source, &mut dest).await.unwrap();
|
||||
};
|
||||
|
||||
rt.block_on(task());
|
||||
})
|
||||
c.bench_function("copy_chunk_to_slow_hdd", |b| {
|
||||
b.iter(|| {
|
||||
let task = || async {
|
||||
let mut source =
|
||||
ChunkReader::new(CHUNK_SIZE, READ_SERVICE_PERIOD).take(SOURCE_SIZE);
|
||||
let mut dest = SlowHddWriter::new(WRITE_SERVICE_PERIOD, WRITE_BUFFER);
|
||||
copy(&mut source, &mut dest).await.unwrap();
|
||||
};
|
||||
|
||||
rt.block_on(task());
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
benchmark_group!(
|
||||
criterion_group!(
|
||||
copy_bench,
|
||||
copy_mem_to_mem,
|
||||
copy_mem_to_slow_hdd,
|
||||
copy_chunk_to_mem,
|
||||
copy_chunk_to_slow_hdd,
|
||||
);
|
||||
benchmark_main!(copy_bench);
|
||||
criterion_main!(copy_bench);
|
||||
|
||||
+66
-57
@@ -6,7 +6,7 @@ use tokio::fs::File;
|
||||
use tokio::io::AsyncReadExt;
|
||||
use tokio_util::codec::{BytesCodec, FramedRead /*FramedWrite*/};
|
||||
|
||||
use bencher::{benchmark_group, benchmark_main, Bencher};
|
||||
use criterion::{criterion_group, criterion_main, Criterion};
|
||||
|
||||
use std::fs::File as StdFile;
|
||||
use std::io::Read as StdRead;
|
||||
@@ -23,81 +23,90 @@ const BLOCK_COUNT: usize = 1_000;
|
||||
const BUFFER_SIZE: usize = 4096;
|
||||
const DEV_ZERO: &str = "/dev/zero";
|
||||
|
||||
fn async_read_codec(b: &mut Bencher) {
|
||||
fn async_read_codec(c: &mut Criterion) {
|
||||
let rt = rt();
|
||||
|
||||
b.iter(|| {
|
||||
let task = || async {
|
||||
let file = File::open(DEV_ZERO).await.unwrap();
|
||||
let mut input_stream = FramedRead::with_capacity(file, BytesCodec::new(), BUFFER_SIZE);
|
||||
c.bench_function("async_read_codec", |b| {
|
||||
b.iter(|| {
|
||||
let task = || async {
|
||||
let file = File::open(DEV_ZERO).await.unwrap();
|
||||
let mut input_stream =
|
||||
FramedRead::with_capacity(file, BytesCodec::new(), BUFFER_SIZE);
|
||||
|
||||
for _i in 0..BLOCK_COUNT {
|
||||
let _bytes = input_stream.next().await.unwrap();
|
||||
}
|
||||
};
|
||||
|
||||
rt.block_on(task());
|
||||
});
|
||||
}
|
||||
|
||||
fn async_read_buf(b: &mut Bencher) {
|
||||
let rt = rt();
|
||||
|
||||
b.iter(|| {
|
||||
let task = || async {
|
||||
let mut file = File::open(DEV_ZERO).await.unwrap();
|
||||
let mut buffer = [0u8; BUFFER_SIZE];
|
||||
|
||||
for _i in 0..BLOCK_COUNT {
|
||||
let count = file.read(&mut buffer).await.unwrap();
|
||||
if count == 0 {
|
||||
break;
|
||||
for _i in 0..BLOCK_COUNT {
|
||||
let _bytes = input_stream.next().await.unwrap();
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
rt.block_on(task());
|
||||
rt.block_on(task());
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn async_read_std_file(b: &mut Bencher) {
|
||||
fn async_read_buf(c: &mut Criterion) {
|
||||
let rt = rt();
|
||||
|
||||
let task = || async {
|
||||
let mut file = tokio::task::block_in_place(|| Box::pin(StdFile::open(DEV_ZERO).unwrap()));
|
||||
c.bench_function("async_read_buf", |b| {
|
||||
b.iter(|| {
|
||||
let task = || async {
|
||||
let mut file = File::open(DEV_ZERO).await.unwrap();
|
||||
let mut buffer = [0u8; BUFFER_SIZE];
|
||||
|
||||
for _i in 0..BLOCK_COUNT {
|
||||
for _i in 0..BLOCK_COUNT {
|
||||
let count = file.read(&mut buffer).await.unwrap();
|
||||
if count == 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
rt.block_on(task());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
fn async_read_std_file(c: &mut Criterion) {
|
||||
let rt = rt();
|
||||
|
||||
c.bench_function("async_read_std_file", |b| {
|
||||
b.iter(|| {
|
||||
let task = || async {
|
||||
let mut file =
|
||||
tokio::task::block_in_place(|| Box::pin(StdFile::open(DEV_ZERO).unwrap()));
|
||||
|
||||
for _i in 0..BLOCK_COUNT {
|
||||
let mut buffer = [0u8; BUFFER_SIZE];
|
||||
let mut file_ref = file.as_mut();
|
||||
|
||||
tokio::task::block_in_place(move || {
|
||||
file_ref.read_exact(&mut buffer).unwrap();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
rt.block_on(task());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
fn sync_read(c: &mut Criterion) {
|
||||
c.bench_function("sync_read", |b| {
|
||||
b.iter(|| {
|
||||
let mut file = StdFile::open(DEV_ZERO).unwrap();
|
||||
let mut buffer = [0u8; BUFFER_SIZE];
|
||||
let mut file_ref = file.as_mut();
|
||||
|
||||
tokio::task::block_in_place(move || {
|
||||
file_ref.read_exact(&mut buffer).unwrap();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
b.iter(|| {
|
||||
rt.block_on(task());
|
||||
for _i in 0..BLOCK_COUNT {
|
||||
file.read_exact(&mut buffer).unwrap();
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn sync_read(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
let mut file = StdFile::open(DEV_ZERO).unwrap();
|
||||
let mut buffer = [0u8; BUFFER_SIZE];
|
||||
|
||||
for _i in 0..BLOCK_COUNT {
|
||||
file.read_exact(&mut buffer).unwrap();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
benchmark_group!(
|
||||
criterion_group!(
|
||||
file,
|
||||
async_read_std_file,
|
||||
async_read_buf,
|
||||
async_read_codec,
|
||||
sync_read
|
||||
);
|
||||
|
||||
benchmark_main!(file);
|
||||
criterion_main!(file);
|
||||
|
||||
@@ -4,46 +4,50 @@
|
||||
|
||||
use tokio::runtime::{self, Runtime};
|
||||
|
||||
use bencher::{benchmark_group, benchmark_main, Bencher};
|
||||
use criterion::{criterion_group, criterion_main, Criterion};
|
||||
|
||||
const NUM_SPAWN: usize = 1_000;
|
||||
|
||||
fn spawn_many_local(b: &mut Bencher) {
|
||||
fn rt_curr_spawn_many_local(c: &mut Criterion) {
|
||||
let rt = rt();
|
||||
let mut handles = Vec::with_capacity(NUM_SPAWN);
|
||||
|
||||
b.iter(|| {
|
||||
rt.block_on(async {
|
||||
for _ in 0..NUM_SPAWN {
|
||||
handles.push(tokio::spawn(async move {}));
|
||||
}
|
||||
c.bench_function("spawn_many_local", |b| {
|
||||
b.iter(|| {
|
||||
rt.block_on(async {
|
||||
for _ in 0..NUM_SPAWN {
|
||||
handles.push(tokio::spawn(async move {}));
|
||||
}
|
||||
|
||||
for handle in handles.drain(..) {
|
||||
handle.await.unwrap();
|
||||
}
|
||||
});
|
||||
for handle in handles.drain(..) {
|
||||
handle.await.unwrap();
|
||||
}
|
||||
});
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn spawn_many_remote_idle(b: &mut Bencher) {
|
||||
fn rt_curr_spawn_many_remote_idle(c: &mut Criterion) {
|
||||
let rt = rt();
|
||||
let rt_handle = rt.handle();
|
||||
let mut handles = Vec::with_capacity(NUM_SPAWN);
|
||||
|
||||
b.iter(|| {
|
||||
for _ in 0..NUM_SPAWN {
|
||||
handles.push(rt_handle.spawn(async {}));
|
||||
}
|
||||
|
||||
rt.block_on(async {
|
||||
for handle in handles.drain(..) {
|
||||
handle.await.unwrap();
|
||||
c.bench_function("spawn_many_remote_idle", |b| {
|
||||
b.iter(|| {
|
||||
for _ in 0..NUM_SPAWN {
|
||||
handles.push(rt_handle.spawn(async {}));
|
||||
}
|
||||
});
|
||||
|
||||
rt.block_on(async {
|
||||
for handle in handles.drain(..) {
|
||||
handle.await.unwrap();
|
||||
}
|
||||
});
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn spawn_many_remote_busy(b: &mut Bencher) {
|
||||
fn rt_curr_spawn_many_remote_busy(c: &mut Criterion) {
|
||||
let rt = rt();
|
||||
let rt_handle = rt.handle();
|
||||
let mut handles = Vec::with_capacity(NUM_SPAWN);
|
||||
@@ -56,16 +60,18 @@ fn spawn_many_remote_busy(b: &mut Bencher) {
|
||||
iter()
|
||||
});
|
||||
|
||||
b.iter(|| {
|
||||
for _ in 0..NUM_SPAWN {
|
||||
handles.push(rt_handle.spawn(async {}));
|
||||
}
|
||||
|
||||
rt.block_on(async {
|
||||
for handle in handles.drain(..) {
|
||||
handle.await.unwrap();
|
||||
c.bench_function("spawn_many_remote_busy", |b| {
|
||||
b.iter(|| {
|
||||
for _ in 0..NUM_SPAWN {
|
||||
handles.push(rt_handle.spawn(async {}));
|
||||
}
|
||||
});
|
||||
|
||||
rt.block_on(async {
|
||||
for handle in handles.drain(..) {
|
||||
handle.await.unwrap();
|
||||
}
|
||||
});
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
@@ -73,11 +79,11 @@ fn rt() -> Runtime {
|
||||
runtime::Builder::new_current_thread().build().unwrap()
|
||||
}
|
||||
|
||||
benchmark_group!(
|
||||
scheduler,
|
||||
spawn_many_local,
|
||||
spawn_many_remote_idle,
|
||||
spawn_many_remote_busy
|
||||
criterion_group!(
|
||||
rt_curr_scheduler,
|
||||
rt_curr_spawn_many_local,
|
||||
rt_curr_spawn_many_remote_idle,
|
||||
rt_curr_spawn_many_remote_busy
|
||||
);
|
||||
|
||||
benchmark_main!(scheduler);
|
||||
criterion_main!(rt_curr_scheduler);
|
||||
|
||||
+125
-112
@@ -5,63 +5,68 @@
|
||||
use tokio::runtime::{self, Runtime};
|
||||
use tokio::sync::oneshot;
|
||||
|
||||
use bencher::{benchmark_group, benchmark_main, Bencher};
|
||||
use std::sync::atomic::Ordering::Relaxed;
|
||||
use std::sync::atomic::{AtomicBool, AtomicUsize};
|
||||
use std::sync::{mpsc, Arc};
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use criterion::{criterion_group, criterion_main, Criterion};
|
||||
|
||||
const NUM_WORKERS: usize = 4;
|
||||
const NUM_SPAWN: usize = 10_000;
|
||||
const STALL_DUR: Duration = Duration::from_micros(10);
|
||||
|
||||
fn spawn_many_local(b: &mut Bencher) {
|
||||
fn rt_multi_spawn_many_local(c: &mut Criterion) {
|
||||
let rt = rt();
|
||||
|
||||
let (tx, rx) = mpsc::sync_channel(1000);
|
||||
let rem = Arc::new(AtomicUsize::new(0));
|
||||
|
||||
b.iter(|| {
|
||||
rem.store(NUM_SPAWN, Relaxed);
|
||||
c.bench_function("spawn_many_local", |b| {
|
||||
b.iter(|| {
|
||||
rem.store(NUM_SPAWN, Relaxed);
|
||||
|
||||
rt.block_on(async {
|
||||
for _ in 0..NUM_SPAWN {
|
||||
let tx = tx.clone();
|
||||
let rem = rem.clone();
|
||||
rt.block_on(async {
|
||||
for _ in 0..NUM_SPAWN {
|
||||
let tx = tx.clone();
|
||||
let rem = rem.clone();
|
||||
|
||||
tokio::spawn(async move {
|
||||
if 1 == rem.fetch_sub(1, Relaxed) {
|
||||
tx.send(()).unwrap();
|
||||
}
|
||||
});
|
||||
}
|
||||
tokio::spawn(async move {
|
||||
if 1 == rem.fetch_sub(1, Relaxed) {
|
||||
tx.send(()).unwrap();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let _ = rx.recv().unwrap();
|
||||
});
|
||||
rx.recv().unwrap();
|
||||
});
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn spawn_many_remote_idle(b: &mut Bencher) {
|
||||
fn rt_multi_spawn_many_remote_idle(c: &mut Criterion) {
|
||||
let rt = rt();
|
||||
|
||||
let mut handles = Vec::with_capacity(NUM_SPAWN);
|
||||
|
||||
b.iter(|| {
|
||||
for _ in 0..NUM_SPAWN {
|
||||
handles.push(rt.spawn(async {}));
|
||||
}
|
||||
|
||||
rt.block_on(async {
|
||||
for handle in handles.drain(..) {
|
||||
handle.await.unwrap();
|
||||
c.bench_function("spawn_many_remote_idle", |b| {
|
||||
b.iter(|| {
|
||||
for _ in 0..NUM_SPAWN {
|
||||
handles.push(rt.spawn(async {}));
|
||||
}
|
||||
});
|
||||
|
||||
rt.block_on(async {
|
||||
for handle in handles.drain(..) {
|
||||
handle.await.unwrap();
|
||||
}
|
||||
});
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
// The runtime is busy with tasks that consume CPU time and yield. Yielding is a
|
||||
// lower notification priority than spawning / regular notification.
|
||||
fn spawn_many_remote_busy1(b: &mut Bencher) {
|
||||
fn rt_multi_spawn_many_remote_busy1(c: &mut Criterion) {
|
||||
let rt = rt();
|
||||
let rt_handle = rt.handle();
|
||||
let mut handles = Vec::with_capacity(NUM_SPAWN);
|
||||
@@ -78,16 +83,18 @@ fn spawn_many_remote_busy1(b: &mut Bencher) {
|
||||
});
|
||||
}
|
||||
|
||||
b.iter(|| {
|
||||
for _ in 0..NUM_SPAWN {
|
||||
handles.push(rt_handle.spawn(async {}));
|
||||
}
|
||||
|
||||
rt.block_on(async {
|
||||
for handle in handles.drain(..) {
|
||||
handle.await.unwrap();
|
||||
c.bench_function("spawn_many_remote_busy1", |b| {
|
||||
b.iter(|| {
|
||||
for _ in 0..NUM_SPAWN {
|
||||
handles.push(rt_handle.spawn(async {}));
|
||||
}
|
||||
});
|
||||
|
||||
rt.block_on(async {
|
||||
for handle in handles.drain(..) {
|
||||
handle.await.unwrap();
|
||||
}
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
flag.store(false, Relaxed);
|
||||
@@ -95,7 +102,7 @@ fn spawn_many_remote_busy1(b: &mut Bencher) {
|
||||
|
||||
// The runtime is busy with tasks that consume CPU time and spawn new high-CPU
|
||||
// tasks. Spawning goes via a higher notification priority than yielding.
|
||||
fn spawn_many_remote_busy2(b: &mut Bencher) {
|
||||
fn rt_multi_spawn_many_remote_busy2(c: &mut Criterion) {
|
||||
const NUM_SPAWN: usize = 1_000;
|
||||
|
||||
let rt = rt();
|
||||
@@ -119,49 +126,52 @@ fn spawn_many_remote_busy2(b: &mut Bencher) {
|
||||
});
|
||||
}
|
||||
|
||||
b.iter(|| {
|
||||
for _ in 0..NUM_SPAWN {
|
||||
handles.push(rt_handle.spawn(async {}));
|
||||
}
|
||||
|
||||
rt.block_on(async {
|
||||
for handle in handles.drain(..) {
|
||||
handle.await.unwrap();
|
||||
c.bench_function("spawn_many_remote_busy2", |b| {
|
||||
b.iter(|| {
|
||||
for _ in 0..NUM_SPAWN {
|
||||
handles.push(rt_handle.spawn(async {}));
|
||||
}
|
||||
});
|
||||
|
||||
rt.block_on(async {
|
||||
for handle in handles.drain(..) {
|
||||
handle.await.unwrap();
|
||||
}
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
flag.store(false, Relaxed);
|
||||
}
|
||||
|
||||
fn yield_many(b: &mut Bencher) {
|
||||
fn rt_multi_yield_many(c: &mut Criterion) {
|
||||
const NUM_YIELD: usize = 1_000;
|
||||
const TASKS: usize = 200;
|
||||
|
||||
let rt = rt();
|
||||
c.bench_function("yield_many", |b| {
|
||||
let rt = rt();
|
||||
let (tx, rx) = mpsc::sync_channel(TASKS);
|
||||
|
||||
let (tx, rx) = mpsc::sync_channel(TASKS);
|
||||
b.iter(move || {
|
||||
for _ in 0..TASKS {
|
||||
let tx = tx.clone();
|
||||
|
||||
b.iter(move || {
|
||||
for _ in 0..TASKS {
|
||||
let tx = tx.clone();
|
||||
rt.spawn(async move {
|
||||
for _ in 0..NUM_YIELD {
|
||||
tokio::task::yield_now().await;
|
||||
}
|
||||
|
||||
rt.spawn(async move {
|
||||
for _ in 0..NUM_YIELD {
|
||||
tokio::task::yield_now().await;
|
||||
}
|
||||
tx.send(()).unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
tx.send(()).unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
for _ in 0..TASKS {
|
||||
let _ = rx.recv().unwrap();
|
||||
}
|
||||
for _ in 0..TASKS {
|
||||
rx.recv().unwrap();
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn ping_pong(b: &mut Bencher) {
|
||||
fn rt_multi_ping_pong(c: &mut Criterion) {
|
||||
const NUM_PINGS: usize = 1_000;
|
||||
|
||||
let rt = rt();
|
||||
@@ -169,46 +179,46 @@ fn ping_pong(b: &mut Bencher) {
|
||||
let (done_tx, done_rx) = mpsc::sync_channel(1000);
|
||||
let rem = Arc::new(AtomicUsize::new(0));
|
||||
|
||||
b.iter(|| {
|
||||
let done_tx = done_tx.clone();
|
||||
let rem = rem.clone();
|
||||
rem.store(NUM_PINGS, Relaxed);
|
||||
c.bench_function("ping_pong", |b| {
|
||||
b.iter(|| {
|
||||
let done_tx = done_tx.clone();
|
||||
let rem = rem.clone();
|
||||
rem.store(NUM_PINGS, Relaxed);
|
||||
|
||||
rt.block_on(async {
|
||||
tokio::spawn(async move {
|
||||
for _ in 0..NUM_PINGS {
|
||||
let rem = rem.clone();
|
||||
let done_tx = done_tx.clone();
|
||||
|
||||
tokio::spawn(async move {
|
||||
let (tx1, rx1) = oneshot::channel();
|
||||
let (tx2, rx2) = oneshot::channel();
|
||||
rt.block_on(async {
|
||||
tokio::spawn(async move {
|
||||
for _ in 0..NUM_PINGS {
|
||||
let rem = rem.clone();
|
||||
let done_tx = done_tx.clone();
|
||||
|
||||
tokio::spawn(async move {
|
||||
rx1.await.unwrap();
|
||||
tx2.send(()).unwrap();
|
||||
let (tx1, rx1) = oneshot::channel();
|
||||
let (tx2, rx2) = oneshot::channel();
|
||||
|
||||
tokio::spawn(async move {
|
||||
rx1.await.unwrap();
|
||||
tx2.send(()).unwrap();
|
||||
});
|
||||
|
||||
tx1.send(()).unwrap();
|
||||
rx2.await.unwrap();
|
||||
|
||||
if 1 == rem.fetch_sub(1, Relaxed) {
|
||||
done_tx.send(()).unwrap();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
tx1.send(()).unwrap();
|
||||
rx2.await.unwrap();
|
||||
|
||||
if 1 == rem.fetch_sub(1, Relaxed) {
|
||||
done_tx.send(()).unwrap();
|
||||
}
|
||||
});
|
||||
}
|
||||
done_rx.recv().unwrap();
|
||||
});
|
||||
|
||||
done_rx.recv().unwrap();
|
||||
});
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn chained_spawn(b: &mut Bencher) {
|
||||
fn rt_multi_chained_spawn(c: &mut Criterion) {
|
||||
const ITER: usize = 1_000;
|
||||
|
||||
let rt = rt();
|
||||
|
||||
fn iter(done_tx: mpsc::SyncSender<()>, n: usize) {
|
||||
if n == 0 {
|
||||
done_tx.send(()).unwrap();
|
||||
@@ -219,18 +229,21 @@ fn chained_spawn(b: &mut Bencher) {
|
||||
}
|
||||
}
|
||||
|
||||
let (done_tx, done_rx) = mpsc::sync_channel(1000);
|
||||
c.bench_function("chained_spawn", |b| {
|
||||
let rt = rt();
|
||||
let (done_tx, done_rx) = mpsc::sync_channel(1000);
|
||||
|
||||
b.iter(move || {
|
||||
let done_tx = done_tx.clone();
|
||||
b.iter(move || {
|
||||
let done_tx = done_tx.clone();
|
||||
|
||||
rt.block_on(async {
|
||||
tokio::spawn(async move {
|
||||
iter(done_tx, ITER);
|
||||
rt.block_on(async {
|
||||
tokio::spawn(async move {
|
||||
iter(done_tx, ITER);
|
||||
});
|
||||
|
||||
done_rx.recv().unwrap();
|
||||
});
|
||||
|
||||
done_rx.recv().unwrap();
|
||||
});
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
@@ -249,15 +262,15 @@ fn stall() {
|
||||
}
|
||||
}
|
||||
|
||||
benchmark_group!(
|
||||
scheduler,
|
||||
spawn_many_local,
|
||||
spawn_many_remote_idle,
|
||||
spawn_many_remote_busy1,
|
||||
spawn_many_remote_busy2,
|
||||
ping_pong,
|
||||
yield_many,
|
||||
chained_spawn,
|
||||
criterion_group!(
|
||||
rt_multi_scheduler,
|
||||
rt_multi_spawn_many_local,
|
||||
rt_multi_spawn_many_remote_idle,
|
||||
rt_multi_spawn_many_remote_busy1,
|
||||
rt_multi_spawn_many_remote_busy2,
|
||||
rt_multi_ping_pong,
|
||||
rt_multi_yield_many,
|
||||
rt_multi_chained_spawn,
|
||||
);
|
||||
|
||||
benchmark_main!(scheduler);
|
||||
criterion_main!(rt_multi_scheduler);
|
||||
|
||||
+17
-15
@@ -1,7 +1,7 @@
|
||||
//! Benchmark the delay in propagating OS signals to any listeners.
|
||||
#![cfg(unix)]
|
||||
|
||||
use bencher::{benchmark_group, benchmark_main, Bencher};
|
||||
use criterion::{criterion_group, criterion_main, Criterion};
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
@@ -41,7 +41,7 @@ pub fn send_signal(signal: libc::c_int) {
|
||||
}
|
||||
}
|
||||
|
||||
fn many_signals(bench: &mut Bencher) {
|
||||
fn many_signals(c: &mut Criterion) {
|
||||
let num_signals = 10;
|
||||
let (tx, mut rx) = mpsc::channel(num_signals);
|
||||
|
||||
@@ -75,21 +75,23 @@ fn many_signals(bench: &mut Bencher) {
|
||||
// tasks have been polled at least once
|
||||
rt.block_on(Spinner::new());
|
||||
|
||||
bench.iter(|| {
|
||||
rt.block_on(async {
|
||||
send_signal(libc::SIGCHLD);
|
||||
for _ in 0..num_signals {
|
||||
rx.recv().await.expect("channel closed");
|
||||
}
|
||||
c.bench_function("many_signals", |b| {
|
||||
b.iter(|| {
|
||||
rt.block_on(async {
|
||||
send_signal(libc::SIGCHLD);
|
||||
for _ in 0..num_signals {
|
||||
rx.recv().await.expect("channel closed");
|
||||
}
|
||||
|
||||
send_signal(libc::SIGIO);
|
||||
for _ in 0..num_signals {
|
||||
rx.recv().await.expect("channel closed");
|
||||
}
|
||||
});
|
||||
send_signal(libc::SIGIO);
|
||||
for _ in 0..num_signals {
|
||||
rx.recv().await.expect("channel closed");
|
||||
}
|
||||
});
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
benchmark_group!(signal_group, many_signals,);
|
||||
criterion_group!(signal_group, many_signals);
|
||||
|
||||
benchmark_main!(signal_group);
|
||||
criterion_main!(signal_group);
|
||||
|
||||
+67
-55
@@ -2,10 +2,7 @@
|
||||
//! This essentially measure the time to enqueue a task in the local and remote
|
||||
//! case.
|
||||
|
||||
#[macro_use]
|
||||
extern crate bencher;
|
||||
|
||||
use bencher::{black_box, Bencher};
|
||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||
|
||||
async fn work() -> usize {
|
||||
let val = 1 + 1;
|
||||
@@ -13,67 +10,82 @@ async fn work() -> usize {
|
||||
black_box(val)
|
||||
}
|
||||
|
||||
fn basic_scheduler_spawn(bench: &mut Bencher) {
|
||||
let runtime = tokio::runtime::Builder::new_current_thread()
|
||||
fn single_rt() -> tokio::runtime::Runtime {
|
||||
tokio::runtime::Builder::new_current_thread()
|
||||
.build()
|
||||
.unwrap();
|
||||
bench.iter(|| {
|
||||
runtime.block_on(async {
|
||||
let h = tokio::spawn(work());
|
||||
assert_eq!(h.await.unwrap(), 2);
|
||||
});
|
||||
});
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn basic_scheduler_spawn10(bench: &mut Bencher) {
|
||||
let runtime = tokio::runtime::Builder::new_current_thread()
|
||||
.build()
|
||||
.unwrap();
|
||||
bench.iter(|| {
|
||||
runtime.block_on(async {
|
||||
let mut handles = Vec::with_capacity(10);
|
||||
for _ in 0..10 {
|
||||
handles.push(tokio::spawn(work()));
|
||||
}
|
||||
for handle in handles {
|
||||
assert_eq!(handle.await.unwrap(), 2);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
fn threaded_scheduler_spawn(bench: &mut Bencher) {
|
||||
let runtime = tokio::runtime::Builder::new_multi_thread()
|
||||
fn multi_rt() -> tokio::runtime::Runtime {
|
||||
tokio::runtime::Builder::new_multi_thread()
|
||||
.worker_threads(1)
|
||||
.build()
|
||||
.unwrap();
|
||||
bench.iter(|| {
|
||||
runtime.block_on(async {
|
||||
let h = tokio::spawn(work());
|
||||
assert_eq!(h.await.unwrap(), 2);
|
||||
});
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn basic_scheduler_spawn(c: &mut Criterion) {
|
||||
let runtime = single_rt();
|
||||
|
||||
c.bench_function("basic_scheduler_spawn", |b| {
|
||||
b.iter(|| {
|
||||
runtime.block_on(async {
|
||||
let h = tokio::spawn(work());
|
||||
assert_eq!(h.await.unwrap(), 2);
|
||||
});
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn threaded_scheduler_spawn10(bench: &mut Bencher) {
|
||||
let runtime = tokio::runtime::Builder::new_multi_thread()
|
||||
.worker_threads(1)
|
||||
.build()
|
||||
.unwrap();
|
||||
bench.iter(|| {
|
||||
runtime.block_on(async {
|
||||
let mut handles = Vec::with_capacity(10);
|
||||
for _ in 0..10 {
|
||||
handles.push(tokio::spawn(work()));
|
||||
}
|
||||
for handle in handles {
|
||||
assert_eq!(handle.await.unwrap(), 2);
|
||||
}
|
||||
});
|
||||
fn basic_scheduler_spawn10(c: &mut Criterion) {
|
||||
let runtime = single_rt();
|
||||
|
||||
c.bench_function("basic_scheduler_spawn10", |b| {
|
||||
b.iter(|| {
|
||||
runtime.block_on(async {
|
||||
let mut handles = Vec::with_capacity(10);
|
||||
for _ in 0..10 {
|
||||
handles.push(tokio::spawn(work()));
|
||||
}
|
||||
for handle in handles {
|
||||
assert_eq!(handle.await.unwrap(), 2);
|
||||
}
|
||||
});
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
bencher::benchmark_group!(
|
||||
fn threaded_scheduler_spawn(c: &mut Criterion) {
|
||||
let runtime = multi_rt();
|
||||
|
||||
c.bench_function("threaded_scheduler_spawn", |b| {
|
||||
b.iter(|| {
|
||||
runtime.block_on(async {
|
||||
let h = tokio::spawn(work());
|
||||
assert_eq!(h.await.unwrap(), 2);
|
||||
});
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn threaded_scheduler_spawn10(c: &mut Criterion) {
|
||||
let runtime = multi_rt();
|
||||
|
||||
c.bench_function("threaded_scheduler_spawn10", |b| {
|
||||
b.iter(|| {
|
||||
runtime.block_on(async {
|
||||
let mut handles = Vec::with_capacity(10);
|
||||
for _ in 0..10 {
|
||||
handles.push(tokio::spawn(work()));
|
||||
}
|
||||
for handle in handles {
|
||||
assert_eq!(handle.await.unwrap(), 2);
|
||||
}
|
||||
});
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group!(
|
||||
spawn,
|
||||
basic_scheduler_spawn,
|
||||
basic_scheduler_spawn10,
|
||||
@@ -81,4 +93,4 @@ bencher::benchmark_group!(
|
||||
threaded_scheduler_spawn10,
|
||||
);
|
||||
|
||||
bencher::benchmark_main!(spawn);
|
||||
criterion_main!(spawn);
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
use rand::{Rng, RngCore, SeedableRng};
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::{broadcast, Notify};
|
||||
|
||||
use criterion::measurement::WallTime;
|
||||
use criterion::{black_box, criterion_group, criterion_main, BenchmarkGroup, Criterion};
|
||||
|
||||
fn rt() -> tokio::runtime::Runtime {
|
||||
tokio::runtime::Builder::new_multi_thread()
|
||||
.worker_threads(6)
|
||||
.build()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn do_work(rng: &mut impl RngCore) -> u32 {
|
||||
use std::fmt::Write;
|
||||
let mut message = String::new();
|
||||
for i in 1..=10 {
|
||||
let _ = write!(&mut message, " {i}={}", rng.random::<f64>());
|
||||
}
|
||||
message
|
||||
.as_bytes()
|
||||
.iter()
|
||||
.map(|&c| c as u32)
|
||||
.fold(0, u32::wrapping_add)
|
||||
}
|
||||
|
||||
fn contention_impl<const N_TASKS: usize>(g: &mut BenchmarkGroup<WallTime>) {
|
||||
let rt = rt();
|
||||
|
||||
let (tx, _rx) = broadcast::channel::<usize>(1000);
|
||||
let wg = Arc::new((AtomicUsize::new(0), Notify::new()));
|
||||
|
||||
for n in 0..N_TASKS {
|
||||
let wg = wg.clone();
|
||||
let mut rx = tx.subscribe();
|
||||
let mut rng = rand::rngs::StdRng::seed_from_u64(n as u64);
|
||||
rt.spawn(async move {
|
||||
while (rx.recv().await).is_ok() {
|
||||
let r = do_work(&mut rng);
|
||||
let _ = black_box(r);
|
||||
if wg.0.fetch_sub(1, Ordering::Relaxed) == 1 {
|
||||
wg.1.notify_one();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const N_ITERS: usize = 100;
|
||||
|
||||
g.bench_function(N_TASKS.to_string(), |b| {
|
||||
b.iter(|| {
|
||||
rt.block_on({
|
||||
let wg = wg.clone();
|
||||
let tx = tx.clone();
|
||||
async move {
|
||||
for i in 0..N_ITERS {
|
||||
assert_eq!(wg.0.fetch_add(N_TASKS, Ordering::Relaxed), 0);
|
||||
tx.send(i).unwrap();
|
||||
while wg.0.load(Ordering::Relaxed) > 0 {
|
||||
wg.1.notified().await;
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn bench_contention(c: &mut Criterion) {
|
||||
let mut group = c.benchmark_group("contention");
|
||||
contention_impl::<10>(&mut group);
|
||||
contention_impl::<100>(&mut group);
|
||||
contention_impl::<500>(&mut group);
|
||||
contention_impl::<1000>(&mut group);
|
||||
group.finish();
|
||||
}
|
||||
|
||||
criterion_group!(contention, bench_contention);
|
||||
|
||||
criterion_main!(contention);
|
||||
+282
-130
@@ -1,8 +1,23 @@
|
||||
use bencher::{black_box, Bencher};
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
type Medium = [usize; 64];
|
||||
type Large = [Medium; 64];
|
||||
use criterion::measurement::WallTime;
|
||||
use criterion::{black_box, criterion_group, criterion_main, BenchmarkGroup, Criterion};
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
struct Medium(#[allow(dead_code)] [usize; 64]);
|
||||
impl Default for Medium {
|
||||
fn default() -> Self {
|
||||
Medium([0; 64])
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
struct Large(#[allow(dead_code)] [Medium; 64]);
|
||||
impl Default for Large {
|
||||
fn default() -> Self {
|
||||
Large([Medium::default(); 64])
|
||||
}
|
||||
}
|
||||
|
||||
fn rt() -> tokio::runtime::Runtime {
|
||||
tokio::runtime::Builder::new_multi_thread()
|
||||
@@ -11,169 +26,306 @@ fn rt() -> tokio::runtime::Runtime {
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn create_1_medium(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
black_box(&mpsc::channel::<Medium>(1));
|
||||
});
|
||||
}
|
||||
|
||||
fn create_100_medium(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
black_box(&mpsc::channel::<Medium>(100));
|
||||
});
|
||||
}
|
||||
|
||||
fn create_100_000_medium(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
black_box(&mpsc::channel::<Medium>(100_000));
|
||||
});
|
||||
}
|
||||
|
||||
fn send_medium(b: &mut Bencher) {
|
||||
let rt = rt();
|
||||
|
||||
b.iter(|| {
|
||||
let (tx, mut rx) = mpsc::channel::<Medium>(1000);
|
||||
|
||||
let _ = rt.block_on(tx.send([0; 64]));
|
||||
|
||||
rt.block_on(rx.recv()).unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
fn send_large(b: &mut Bencher) {
|
||||
let rt = rt();
|
||||
|
||||
b.iter(|| {
|
||||
let (tx, mut rx) = mpsc::channel::<Large>(1000);
|
||||
|
||||
let _ = rt.block_on(tx.send([[0; 64]; 64]));
|
||||
|
||||
rt.block_on(rx.recv()).unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
fn contention_bounded(b: &mut Bencher) {
|
||||
let rt = rt();
|
||||
|
||||
b.iter(|| {
|
||||
rt.block_on(async move {
|
||||
let (tx, mut rx) = mpsc::channel::<usize>(1_000_000);
|
||||
|
||||
for _ in 0..5 {
|
||||
let tx = tx.clone();
|
||||
tokio::spawn(async move {
|
||||
for i in 0..1000 {
|
||||
tx.send(i).await.unwrap();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
for _ in 0..1_000 * 5 {
|
||||
let _ = rx.recv().await;
|
||||
}
|
||||
fn create_medium<const SIZE: usize>(g: &mut BenchmarkGroup<WallTime>) {
|
||||
g.bench_function(SIZE.to_string(), |b| {
|
||||
b.iter(|| {
|
||||
black_box(&mpsc::channel::<Medium>(SIZE));
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn contention_bounded_full(b: &mut Bencher) {
|
||||
fn send_data<T: Default, const SIZE: usize>(g: &mut BenchmarkGroup<WallTime>, prefix: &str) {
|
||||
let rt = rt();
|
||||
|
||||
b.iter(|| {
|
||||
rt.block_on(async move {
|
||||
let (tx, mut rx) = mpsc::channel::<usize>(100);
|
||||
g.bench_function(format!("{prefix}_{SIZE}"), |b| {
|
||||
b.iter(|| {
|
||||
let (tx, mut rx) = mpsc::channel::<T>(SIZE);
|
||||
|
||||
for _ in 0..5 {
|
||||
let tx = tx.clone();
|
||||
tokio::spawn(async move {
|
||||
for i in 0..1000 {
|
||||
tx.send(i).await.unwrap();
|
||||
}
|
||||
});
|
||||
}
|
||||
let _ = rt.block_on(tx.send(T::default()));
|
||||
|
||||
for _ in 0..1_000 * 5 {
|
||||
let _ = rx.recv().await;
|
||||
}
|
||||
rt.block_on(rx.recv()).unwrap();
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn contention_unbounded(b: &mut Bencher) {
|
||||
fn contention_bounded(g: &mut BenchmarkGroup<WallTime>) {
|
||||
let rt = rt();
|
||||
|
||||
b.iter(|| {
|
||||
rt.block_on(async move {
|
||||
let (tx, mut rx) = mpsc::unbounded_channel::<usize>();
|
||||
g.bench_function("bounded", |b| {
|
||||
b.iter(|| {
|
||||
rt.block_on(async move {
|
||||
let (tx, mut rx) = mpsc::channel::<usize>(1_000_000);
|
||||
|
||||
for _ in 0..5 {
|
||||
let tx = tx.clone();
|
||||
tokio::spawn(async move {
|
||||
for i in 0..1000 {
|
||||
tx.send(i).unwrap();
|
||||
}
|
||||
});
|
||||
}
|
||||
for _ in 0..5 {
|
||||
let tx = tx.clone();
|
||||
tokio::spawn(async move {
|
||||
for i in 0..1000 {
|
||||
tx.send(i).await.unwrap();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
for _ in 0..1_000 * 5 {
|
||||
let _ = rx.recv().await;
|
||||
}
|
||||
for _ in 0..1_000 * 5 {
|
||||
let _ = rx.recv().await;
|
||||
}
|
||||
})
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn uncontented_bounded(b: &mut Bencher) {
|
||||
fn contention_bounded_recv_many(g: &mut BenchmarkGroup<WallTime>) {
|
||||
let rt = rt();
|
||||
|
||||
b.iter(|| {
|
||||
rt.block_on(async move {
|
||||
let (tx, mut rx) = mpsc::channel::<usize>(1_000_000);
|
||||
g.bench_function("bounded_recv_many", |b| {
|
||||
b.iter(|| {
|
||||
rt.block_on(async move {
|
||||
let (tx, mut rx) = mpsc::channel::<usize>(1_000_000);
|
||||
|
||||
for i in 0..5000 {
|
||||
tx.send(i).await.unwrap();
|
||||
}
|
||||
for _ in 0..5 {
|
||||
let tx = tx.clone();
|
||||
tokio::spawn(async move {
|
||||
for i in 0..1000 {
|
||||
tx.send(i).await.unwrap();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
for _ in 0..5_000 {
|
||||
let _ = rx.recv().await;
|
||||
}
|
||||
let mut buffer = Vec::<usize>::with_capacity(5_000);
|
||||
let mut total = 0;
|
||||
while total < 1_000 * 5 {
|
||||
total += rx.recv_many(&mut buffer, 5_000).await;
|
||||
}
|
||||
})
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn uncontented_unbounded(b: &mut Bencher) {
|
||||
fn contention_bounded_full(g: &mut BenchmarkGroup<WallTime>) {
|
||||
let rt = rt();
|
||||
|
||||
b.iter(|| {
|
||||
rt.block_on(async move {
|
||||
let (tx, mut rx) = mpsc::unbounded_channel::<usize>();
|
||||
g.bench_function("bounded_full", |b| {
|
||||
b.iter(|| {
|
||||
rt.block_on(async move {
|
||||
let (tx, mut rx) = mpsc::channel::<usize>(100);
|
||||
|
||||
for i in 0..5000 {
|
||||
tx.send(i).unwrap();
|
||||
}
|
||||
for _ in 0..5 {
|
||||
let tx = tx.clone();
|
||||
tokio::spawn(async move {
|
||||
for i in 0..1000 {
|
||||
tx.send(i).await.unwrap();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
for _ in 0..5_000 {
|
||||
let _ = rx.recv().await;
|
||||
}
|
||||
for _ in 0..1_000 * 5 {
|
||||
let _ = rx.recv().await;
|
||||
}
|
||||
})
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
bencher::benchmark_group!(
|
||||
create,
|
||||
create_1_medium,
|
||||
create_100_medium,
|
||||
create_100_000_medium
|
||||
);
|
||||
fn contention_bounded_full_recv_many(g: &mut BenchmarkGroup<WallTime>) {
|
||||
let rt = rt();
|
||||
|
||||
bencher::benchmark_group!(send, send_medium, send_large);
|
||||
g.bench_function("bounded_full_recv_many", |b| {
|
||||
b.iter(|| {
|
||||
rt.block_on(async move {
|
||||
let (tx, mut rx) = mpsc::channel::<usize>(100);
|
||||
|
||||
bencher::benchmark_group!(
|
||||
contention,
|
||||
contention_bounded,
|
||||
contention_bounded_full,
|
||||
contention_unbounded,
|
||||
uncontented_bounded,
|
||||
uncontented_unbounded
|
||||
);
|
||||
for _ in 0..5 {
|
||||
let tx = tx.clone();
|
||||
tokio::spawn(async move {
|
||||
for i in 0..1000 {
|
||||
tx.send(i).await.unwrap();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bencher::benchmark_main!(create, send, contention);
|
||||
let mut buffer = Vec::<usize>::with_capacity(5_000);
|
||||
let mut total = 0;
|
||||
while total < 1_000 * 5 {
|
||||
total += rx.recv_many(&mut buffer, 5_000).await;
|
||||
}
|
||||
})
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn contention_unbounded(g: &mut BenchmarkGroup<WallTime>) {
|
||||
let rt = rt();
|
||||
|
||||
g.bench_function("unbounded", |b| {
|
||||
b.iter(|| {
|
||||
rt.block_on(async move {
|
||||
let (tx, mut rx) = mpsc::unbounded_channel::<usize>();
|
||||
|
||||
for _ in 0..5 {
|
||||
let tx = tx.clone();
|
||||
tokio::spawn(async move {
|
||||
for i in 0..1000 {
|
||||
tx.send(i).unwrap();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
for _ in 0..1_000 * 5 {
|
||||
let _ = rx.recv().await;
|
||||
}
|
||||
})
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn contention_unbounded_recv_many(g: &mut BenchmarkGroup<WallTime>) {
|
||||
let rt = rt();
|
||||
|
||||
g.bench_function("unbounded_recv_many", |b| {
|
||||
b.iter(|| {
|
||||
rt.block_on(async move {
|
||||
let (tx, mut rx) = mpsc::unbounded_channel::<usize>();
|
||||
|
||||
for _ in 0..5 {
|
||||
let tx = tx.clone();
|
||||
tokio::spawn(async move {
|
||||
for i in 0..1000 {
|
||||
tx.send(i).unwrap();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let mut buffer = Vec::<usize>::with_capacity(5_000);
|
||||
let mut total = 0;
|
||||
while total < 1_000 * 5 {
|
||||
total += rx.recv_many(&mut buffer, 5_000).await;
|
||||
}
|
||||
})
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn uncontented_bounded(g: &mut BenchmarkGroup<WallTime>) {
|
||||
let rt = rt();
|
||||
|
||||
g.bench_function("bounded", |b| {
|
||||
b.iter(|| {
|
||||
rt.block_on(async move {
|
||||
let (tx, mut rx) = mpsc::channel::<usize>(1_000_000);
|
||||
|
||||
for i in 0..5000 {
|
||||
tx.send(i).await.unwrap();
|
||||
}
|
||||
|
||||
for _ in 0..5_000 {
|
||||
let _ = rx.recv().await;
|
||||
}
|
||||
})
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn uncontented_bounded_recv_many(g: &mut BenchmarkGroup<WallTime>) {
|
||||
let rt = rt();
|
||||
|
||||
g.bench_function("bounded_recv_many", |b| {
|
||||
b.iter(|| {
|
||||
rt.block_on(async move {
|
||||
let (tx, mut rx) = mpsc::channel::<usize>(1_000_000);
|
||||
|
||||
for i in 0..5000 {
|
||||
tx.send(i).await.unwrap();
|
||||
}
|
||||
|
||||
let mut buffer = Vec::<usize>::with_capacity(5_000);
|
||||
let mut total = 0;
|
||||
while total < 1_000 * 5 {
|
||||
total += rx.recv_many(&mut buffer, 5_000).await;
|
||||
}
|
||||
})
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn uncontented_unbounded(g: &mut BenchmarkGroup<WallTime>) {
|
||||
let rt = rt();
|
||||
|
||||
g.bench_function("unbounded", |b| {
|
||||
b.iter(|| {
|
||||
rt.block_on(async move {
|
||||
let (tx, mut rx) = mpsc::unbounded_channel::<usize>();
|
||||
|
||||
for i in 0..5000 {
|
||||
tx.send(i).unwrap();
|
||||
}
|
||||
|
||||
for _ in 0..5_000 {
|
||||
let _ = rx.recv().await;
|
||||
}
|
||||
})
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn uncontented_unbounded_recv_many(g: &mut BenchmarkGroup<WallTime>) {
|
||||
let rt = rt();
|
||||
|
||||
g.bench_function("unbounded_recv_many", |b| {
|
||||
b.iter(|| {
|
||||
rt.block_on(async move {
|
||||
let (tx, mut rx) = mpsc::unbounded_channel::<usize>();
|
||||
|
||||
for i in 0..5000 {
|
||||
tx.send(i).unwrap();
|
||||
}
|
||||
|
||||
let mut buffer = Vec::<usize>::with_capacity(5_000);
|
||||
let mut total = 0;
|
||||
while total < 1_000 * 5 {
|
||||
total += rx.recv_many(&mut buffer, 5_000).await;
|
||||
}
|
||||
})
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn bench_create_medium(c: &mut Criterion) {
|
||||
let mut group = c.benchmark_group("create_medium");
|
||||
create_medium::<1>(&mut group);
|
||||
create_medium::<100>(&mut group);
|
||||
create_medium::<100_000>(&mut group);
|
||||
group.finish();
|
||||
}
|
||||
|
||||
fn bench_send(c: &mut Criterion) {
|
||||
let mut group = c.benchmark_group("send");
|
||||
send_data::<Medium, 1000>(&mut group, "medium");
|
||||
send_data::<Large, 1000>(&mut group, "large");
|
||||
group.finish();
|
||||
}
|
||||
|
||||
fn bench_contention(c: &mut Criterion) {
|
||||
let mut group = c.benchmark_group("contention");
|
||||
contention_bounded(&mut group);
|
||||
contention_bounded_recv_many(&mut group);
|
||||
contention_bounded_full(&mut group);
|
||||
contention_bounded_full_recv_many(&mut group);
|
||||
contention_unbounded(&mut group);
|
||||
contention_unbounded_recv_many(&mut group);
|
||||
group.finish();
|
||||
}
|
||||
|
||||
fn bench_uncontented(c: &mut Criterion) {
|
||||
let mut group = c.benchmark_group("uncontented");
|
||||
uncontented_bounded(&mut group);
|
||||
uncontented_bounded_recv_many(&mut group);
|
||||
uncontented_unbounded(&mut group);
|
||||
uncontented_unbounded_recv_many(&mut group);
|
||||
group.finish();
|
||||
}
|
||||
|
||||
criterion_group!(create, bench_create_medium);
|
||||
criterion_group!(send, bench_send);
|
||||
criterion_group!(contention, bench_contention);
|
||||
criterion_group!(uncontented, bench_uncontented);
|
||||
|
||||
criterion_main!(create, send, contention, uncontented);
|
||||
|
||||
@@ -1,27 +1,28 @@
|
||||
use bencher::{benchmark_group, benchmark_main, Bencher};
|
||||
use tokio::{
|
||||
runtime::Runtime,
|
||||
sync::{mpsc, oneshot},
|
||||
};
|
||||
|
||||
fn request_reply_current_thread(b: &mut Bencher) {
|
||||
use criterion::{criterion_group, criterion_main, Criterion};
|
||||
|
||||
fn request_reply_current_thread(c: &mut Criterion) {
|
||||
let rt = tokio::runtime::Builder::new_current_thread()
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
request_reply(b, rt);
|
||||
request_reply(c, rt);
|
||||
}
|
||||
|
||||
fn request_reply_multi_threaded(b: &mut Bencher) {
|
||||
fn request_reply_multi_threaded(c: &mut Criterion) {
|
||||
let rt = tokio::runtime::Builder::new_multi_thread()
|
||||
.worker_threads(1)
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
request_reply(b, rt);
|
||||
request_reply(c, rt);
|
||||
}
|
||||
|
||||
fn request_reply(b: &mut Bencher, rt: Runtime) {
|
||||
fn request_reply(b: &mut Criterion, rt: Runtime) {
|
||||
let tx = rt.block_on(async move {
|
||||
let (tx, mut rx) = mpsc::channel::<oneshot::Sender<()>>(10);
|
||||
tokio::spawn(async move {
|
||||
@@ -32,22 +33,24 @@ fn request_reply(b: &mut Bencher, rt: Runtime) {
|
||||
tx
|
||||
});
|
||||
|
||||
b.iter(|| {
|
||||
let task_tx = tx.clone();
|
||||
rt.block_on(async move {
|
||||
for _ in 0..1_000 {
|
||||
let (o_tx, o_rx) = oneshot::channel();
|
||||
task_tx.send(o_tx).await.unwrap();
|
||||
let _ = o_rx.await;
|
||||
}
|
||||
b.bench_function("request_reply", |b| {
|
||||
b.iter(|| {
|
||||
let task_tx = tx.clone();
|
||||
rt.block_on(async move {
|
||||
for _ in 0..1_000 {
|
||||
let (o_tx, o_rx) = oneshot::channel();
|
||||
task_tx.send(o_tx).await.unwrap();
|
||||
let _ = o_rx.await;
|
||||
}
|
||||
})
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
benchmark_group!(
|
||||
criterion_group!(
|
||||
sync_mpsc_oneshot_group,
|
||||
request_reply_current_thread,
|
||||
request_reply_multi_threaded,
|
||||
);
|
||||
|
||||
benchmark_main!(sync_mpsc_oneshot_group);
|
||||
criterion_main!(sync_mpsc_oneshot_group);
|
||||
|
||||
+47
-33
@@ -1,9 +1,11 @@
|
||||
use bencher::Bencher;
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::sync::Arc;
|
||||
|
||||
use tokio::sync::Notify;
|
||||
|
||||
use criterion::measurement::WallTime;
|
||||
use criterion::{criterion_group, criterion_main, BenchmarkGroup, Criterion};
|
||||
|
||||
fn rt() -> tokio::runtime::Runtime {
|
||||
tokio::runtime::Builder::new_multi_thread()
|
||||
.worker_threads(6)
|
||||
@@ -11,7 +13,7 @@ fn rt() -> tokio::runtime::Runtime {
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn notify_waiters<const N_WAITERS: usize>(b: &mut Bencher) {
|
||||
fn notify_waiters<const N_WAITERS: usize>(g: &mut BenchmarkGroup<WallTime>) {
|
||||
let rt = rt();
|
||||
let notify = Arc::new(Notify::new());
|
||||
let counter = Arc::new(AtomicUsize::new(0));
|
||||
@@ -29,18 +31,20 @@ fn notify_waiters<const N_WAITERS: usize>(b: &mut Bencher) {
|
||||
}
|
||||
|
||||
const N_ITERS: usize = 500;
|
||||
b.iter(|| {
|
||||
counter.store(0, Ordering::Relaxed);
|
||||
loop {
|
||||
notify.notify_waiters();
|
||||
if counter.load(Ordering::Relaxed) >= N_ITERS {
|
||||
break;
|
||||
g.bench_function(N_WAITERS.to_string(), |b| {
|
||||
b.iter(|| {
|
||||
counter.store(0, Ordering::Relaxed);
|
||||
loop {
|
||||
notify.notify_waiters();
|
||||
if counter.load(Ordering::Relaxed) >= N_ITERS {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn notify_one<const N_WAITERS: usize>(b: &mut Bencher) {
|
||||
fn notify_one<const N_WAITERS: usize>(g: &mut BenchmarkGroup<WallTime>) {
|
||||
let rt = rt();
|
||||
let notify = Arc::new(Notify::new());
|
||||
let counter = Arc::new(AtomicUsize::new(0));
|
||||
@@ -58,33 +62,43 @@ fn notify_one<const N_WAITERS: usize>(b: &mut Bencher) {
|
||||
}
|
||||
|
||||
const N_ITERS: usize = 500;
|
||||
b.iter(|| {
|
||||
counter.store(0, Ordering::Relaxed);
|
||||
loop {
|
||||
notify.notify_one();
|
||||
if counter.load(Ordering::Relaxed) >= N_ITERS {
|
||||
break;
|
||||
g.bench_function(N_WAITERS.to_string(), |b| {
|
||||
b.iter(|| {
|
||||
counter.store(0, Ordering::Relaxed);
|
||||
loop {
|
||||
notify.notify_one();
|
||||
if counter.load(Ordering::Relaxed) >= N_ITERS {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
bencher::benchmark_group!(
|
||||
fn bench_notify_one(c: &mut Criterion) {
|
||||
let mut group = c.benchmark_group("notify_one");
|
||||
notify_one::<10>(&mut group);
|
||||
notify_one::<50>(&mut group);
|
||||
notify_one::<100>(&mut group);
|
||||
notify_one::<200>(&mut group);
|
||||
notify_one::<500>(&mut group);
|
||||
group.finish();
|
||||
}
|
||||
|
||||
fn bench_notify_waiters(c: &mut Criterion) {
|
||||
let mut group = c.benchmark_group("notify_waiters");
|
||||
notify_waiters::<10>(&mut group);
|
||||
notify_waiters::<50>(&mut group);
|
||||
notify_waiters::<100>(&mut group);
|
||||
notify_waiters::<200>(&mut group);
|
||||
notify_waiters::<500>(&mut group);
|
||||
group.finish();
|
||||
}
|
||||
|
||||
criterion_group!(
|
||||
notify_waiters_simple,
|
||||
notify_waiters::<10>,
|
||||
notify_waiters::<50>,
|
||||
notify_waiters::<100>,
|
||||
notify_waiters::<200>,
|
||||
notify_waiters::<500>
|
||||
bench_notify_one,
|
||||
bench_notify_waiters
|
||||
);
|
||||
|
||||
bencher::benchmark_group!(
|
||||
notify_one_simple,
|
||||
notify_one::<10>,
|
||||
notify_one::<50>,
|
||||
notify_one::<100>,
|
||||
notify_one::<200>,
|
||||
notify_one::<500>
|
||||
);
|
||||
|
||||
bencher::benchmark_main!(notify_waiters_simple, notify_one_simple);
|
||||
criterion_main!(notify_waiters_simple);
|
||||
|
||||
+91
-70
@@ -1,26 +1,30 @@
|
||||
use bencher::{black_box, Bencher};
|
||||
use std::sync::Arc;
|
||||
use tokio::{sync::RwLock, task};
|
||||
|
||||
fn read_uncontended(b: &mut Bencher) {
|
||||
use criterion::measurement::WallTime;
|
||||
use criterion::{black_box, criterion_group, criterion_main, BenchmarkGroup, Criterion};
|
||||
|
||||
fn read_uncontended(g: &mut BenchmarkGroup<WallTime>) {
|
||||
let rt = tokio::runtime::Builder::new_multi_thread()
|
||||
.worker_threads(6)
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
let lock = Arc::new(RwLock::new(()));
|
||||
b.iter(|| {
|
||||
let lock = lock.clone();
|
||||
rt.block_on(async move {
|
||||
for _ in 0..6 {
|
||||
let read = lock.read().await;
|
||||
let _read = black_box(read);
|
||||
}
|
||||
g.bench_function("read", |b| {
|
||||
b.iter(|| {
|
||||
let lock = lock.clone();
|
||||
rt.block_on(async move {
|
||||
for _ in 0..6 {
|
||||
let read = lock.read().await;
|
||||
let _read = black_box(read);
|
||||
}
|
||||
})
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn read_concurrent_uncontended_multi(b: &mut Bencher) {
|
||||
fn read_concurrent_uncontended_multi(g: &mut BenchmarkGroup<WallTime>) {
|
||||
let rt = tokio::runtime::Builder::new_multi_thread()
|
||||
.worker_threads(6)
|
||||
.build()
|
||||
@@ -32,23 +36,25 @@ fn read_concurrent_uncontended_multi(b: &mut Bencher) {
|
||||
}
|
||||
|
||||
let lock = Arc::new(RwLock::new(()));
|
||||
b.iter(|| {
|
||||
let lock = lock.clone();
|
||||
rt.block_on(async move {
|
||||
let j = tokio::try_join! {
|
||||
task::spawn(task(lock.clone())),
|
||||
task::spawn(task(lock.clone())),
|
||||
task::spawn(task(lock.clone())),
|
||||
task::spawn(task(lock.clone())),
|
||||
task::spawn(task(lock.clone())),
|
||||
task::spawn(task(lock.clone()))
|
||||
};
|
||||
j.unwrap();
|
||||
g.bench_function("read_concurrent_multi", |b| {
|
||||
b.iter(|| {
|
||||
let lock = lock.clone();
|
||||
rt.block_on(async move {
|
||||
let j = tokio::try_join! {
|
||||
task::spawn(task(lock.clone())),
|
||||
task::spawn(task(lock.clone())),
|
||||
task::spawn(task(lock.clone())),
|
||||
task::spawn(task(lock.clone())),
|
||||
task::spawn(task(lock.clone())),
|
||||
task::spawn(task(lock.clone()))
|
||||
};
|
||||
j.unwrap();
|
||||
})
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn read_concurrent_uncontended(b: &mut Bencher) {
|
||||
fn read_concurrent_uncontended(g: &mut BenchmarkGroup<WallTime>) {
|
||||
let rt = tokio::runtime::Builder::new_current_thread()
|
||||
.build()
|
||||
.unwrap();
|
||||
@@ -59,22 +65,24 @@ fn read_concurrent_uncontended(b: &mut Bencher) {
|
||||
}
|
||||
|
||||
let lock = Arc::new(RwLock::new(()));
|
||||
b.iter(|| {
|
||||
let lock = lock.clone();
|
||||
rt.block_on(async move {
|
||||
tokio::join! {
|
||||
task(lock.clone()),
|
||||
task(lock.clone()),
|
||||
task(lock.clone()),
|
||||
task(lock.clone()),
|
||||
task(lock.clone()),
|
||||
task(lock.clone())
|
||||
};
|
||||
g.bench_function("read_concurrent", |b| {
|
||||
b.iter(|| {
|
||||
let lock = lock.clone();
|
||||
rt.block_on(async move {
|
||||
tokio::join! {
|
||||
task(lock.clone()),
|
||||
task(lock.clone()),
|
||||
task(lock.clone()),
|
||||
task(lock.clone()),
|
||||
task(lock.clone()),
|
||||
task(lock.clone())
|
||||
};
|
||||
})
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn read_concurrent_contended_multi(b: &mut Bencher) {
|
||||
fn read_concurrent_contended_multi(g: &mut BenchmarkGroup<WallTime>) {
|
||||
let rt = tokio::runtime::Builder::new_multi_thread()
|
||||
.worker_threads(6)
|
||||
.build()
|
||||
@@ -86,24 +94,26 @@ fn read_concurrent_contended_multi(b: &mut Bencher) {
|
||||
}
|
||||
|
||||
let lock = Arc::new(RwLock::new(()));
|
||||
b.iter(|| {
|
||||
let lock = lock.clone();
|
||||
rt.block_on(async move {
|
||||
let write = lock.write().await;
|
||||
let j = tokio::try_join! {
|
||||
async move { drop(write); Ok(()) },
|
||||
task::spawn(task(lock.clone())),
|
||||
task::spawn(task(lock.clone())),
|
||||
task::spawn(task(lock.clone())),
|
||||
task::spawn(task(lock.clone())),
|
||||
task::spawn(task(lock.clone())),
|
||||
};
|
||||
j.unwrap();
|
||||
g.bench_function("read_concurrent_multi", |b| {
|
||||
b.iter(|| {
|
||||
let lock = lock.clone();
|
||||
rt.block_on(async move {
|
||||
let write = lock.write().await;
|
||||
let j = tokio::try_join! {
|
||||
async move { drop(write); Ok(()) },
|
||||
task::spawn(task(lock.clone())),
|
||||
task::spawn(task(lock.clone())),
|
||||
task::spawn(task(lock.clone())),
|
||||
task::spawn(task(lock.clone())),
|
||||
task::spawn(task(lock.clone())),
|
||||
};
|
||||
j.unwrap();
|
||||
})
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn read_concurrent_contended(b: &mut Bencher) {
|
||||
fn read_concurrent_contended(g: &mut BenchmarkGroup<WallTime>) {
|
||||
let rt = tokio::runtime::Builder::new_current_thread()
|
||||
.build()
|
||||
.unwrap();
|
||||
@@ -114,29 +124,40 @@ fn read_concurrent_contended(b: &mut Bencher) {
|
||||
}
|
||||
|
||||
let lock = Arc::new(RwLock::new(()));
|
||||
b.iter(|| {
|
||||
let lock = lock.clone();
|
||||
rt.block_on(async move {
|
||||
let write = lock.write().await;
|
||||
tokio::join! {
|
||||
async move { drop(write) },
|
||||
task(lock.clone()),
|
||||
task(lock.clone()),
|
||||
task(lock.clone()),
|
||||
task(lock.clone()),
|
||||
task(lock.clone()),
|
||||
};
|
||||
g.bench_function("read_concurrent", |b| {
|
||||
b.iter(|| {
|
||||
let lock = lock.clone();
|
||||
rt.block_on(async move {
|
||||
let write = lock.write().await;
|
||||
tokio::join! {
|
||||
async move { drop(write) },
|
||||
task(lock.clone()),
|
||||
task(lock.clone()),
|
||||
task(lock.clone()),
|
||||
task(lock.clone()),
|
||||
task(lock.clone()),
|
||||
};
|
||||
})
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
bencher::benchmark_group!(
|
||||
sync_rwlock,
|
||||
read_uncontended,
|
||||
read_concurrent_uncontended,
|
||||
read_concurrent_uncontended_multi,
|
||||
read_concurrent_contended,
|
||||
read_concurrent_contended_multi
|
||||
);
|
||||
fn bench_contention(c: &mut Criterion) {
|
||||
let mut group = c.benchmark_group("contention");
|
||||
read_concurrent_contended(&mut group);
|
||||
read_concurrent_contended_multi(&mut group);
|
||||
group.finish();
|
||||
}
|
||||
|
||||
bencher::benchmark_main!(sync_rwlock);
|
||||
fn bench_uncontented(c: &mut Criterion) {
|
||||
let mut group = c.benchmark_group("uncontented");
|
||||
read_uncontended(&mut group);
|
||||
read_concurrent_uncontended(&mut group);
|
||||
read_concurrent_uncontended_multi(&mut group);
|
||||
group.finish();
|
||||
}
|
||||
|
||||
criterion_group!(contention, bench_contention);
|
||||
criterion_group!(uncontented, bench_uncontented);
|
||||
|
||||
criterion_main!(contention, uncontented);
|
||||
|
||||
+106
-84
@@ -1,21 +1,36 @@
|
||||
use bencher::Bencher;
|
||||
use std::sync::Arc;
|
||||
use tokio::runtime::Runtime;
|
||||
use tokio::{sync::Semaphore, task};
|
||||
|
||||
fn uncontended(b: &mut Bencher) {
|
||||
let rt = tokio::runtime::Builder::new_multi_thread()
|
||||
use criterion::measurement::WallTime;
|
||||
use criterion::{criterion_group, criterion_main, BenchmarkGroup, Criterion};
|
||||
|
||||
fn single_rt() -> Runtime {
|
||||
tokio::runtime::Builder::new_current_thread()
|
||||
.build()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn multi_rt() -> Runtime {
|
||||
tokio::runtime::Builder::new_multi_thread()
|
||||
.worker_threads(6)
|
||||
.build()
|
||||
.unwrap();
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn uncontended(g: &mut BenchmarkGroup<WallTime>) {
|
||||
let rt = multi_rt();
|
||||
|
||||
let s = Arc::new(Semaphore::new(10));
|
||||
b.iter(|| {
|
||||
let s = s.clone();
|
||||
rt.block_on(async move {
|
||||
for _ in 0..6 {
|
||||
let permit = s.acquire().await;
|
||||
drop(permit);
|
||||
}
|
||||
g.bench_function("multi", |b| {
|
||||
b.iter(|| {
|
||||
let s = s.clone();
|
||||
rt.block_on(async move {
|
||||
for _ in 0..6 {
|
||||
let permit = s.acquire().await;
|
||||
drop(permit);
|
||||
}
|
||||
})
|
||||
})
|
||||
});
|
||||
}
|
||||
@@ -25,101 +40,108 @@ async fn task(s: Arc<Semaphore>) {
|
||||
drop(permit);
|
||||
}
|
||||
|
||||
fn uncontended_concurrent_multi(b: &mut Bencher) {
|
||||
let rt = tokio::runtime::Builder::new_multi_thread()
|
||||
.worker_threads(6)
|
||||
.build()
|
||||
.unwrap();
|
||||
fn uncontended_concurrent_multi(g: &mut BenchmarkGroup<WallTime>) {
|
||||
let rt = multi_rt();
|
||||
|
||||
let s = Arc::new(Semaphore::new(10));
|
||||
b.iter(|| {
|
||||
let s = s.clone();
|
||||
rt.block_on(async move {
|
||||
let j = tokio::try_join! {
|
||||
task::spawn(task(s.clone())),
|
||||
task::spawn(task(s.clone())),
|
||||
task::spawn(task(s.clone())),
|
||||
task::spawn(task(s.clone())),
|
||||
task::spawn(task(s.clone())),
|
||||
task::spawn(task(s.clone()))
|
||||
};
|
||||
j.unwrap();
|
||||
g.bench_function("concurrent_multi", |b| {
|
||||
b.iter(|| {
|
||||
let s = s.clone();
|
||||
rt.block_on(async move {
|
||||
let j = tokio::try_join! {
|
||||
task::spawn(task(s.clone())),
|
||||
task::spawn(task(s.clone())),
|
||||
task::spawn(task(s.clone())),
|
||||
task::spawn(task(s.clone())),
|
||||
task::spawn(task(s.clone())),
|
||||
task::spawn(task(s.clone()))
|
||||
};
|
||||
j.unwrap();
|
||||
})
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn uncontended_concurrent_single(b: &mut Bencher) {
|
||||
let rt = tokio::runtime::Builder::new_current_thread()
|
||||
.build()
|
||||
.unwrap();
|
||||
fn uncontended_concurrent_single(g: &mut BenchmarkGroup<WallTime>) {
|
||||
let rt = single_rt();
|
||||
|
||||
let s = Arc::new(Semaphore::new(10));
|
||||
b.iter(|| {
|
||||
let s = s.clone();
|
||||
rt.block_on(async move {
|
||||
tokio::join! {
|
||||
task(s.clone()),
|
||||
task(s.clone()),
|
||||
task(s.clone()),
|
||||
task(s.clone()),
|
||||
task(s.clone()),
|
||||
task(s.clone())
|
||||
};
|
||||
g.bench_function("concurrent_single", |b| {
|
||||
b.iter(|| {
|
||||
let s = s.clone();
|
||||
rt.block_on(async move {
|
||||
tokio::join! {
|
||||
task(s.clone()),
|
||||
task(s.clone()),
|
||||
task(s.clone()),
|
||||
task(s.clone()),
|
||||
task(s.clone()),
|
||||
task(s.clone())
|
||||
};
|
||||
})
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn contended_concurrent_multi(b: &mut Bencher) {
|
||||
let rt = tokio::runtime::Builder::new_multi_thread()
|
||||
.worker_threads(6)
|
||||
.build()
|
||||
.unwrap();
|
||||
fn contended_concurrent_multi(g: &mut BenchmarkGroup<WallTime>) {
|
||||
let rt = multi_rt();
|
||||
|
||||
let s = Arc::new(Semaphore::new(5));
|
||||
b.iter(|| {
|
||||
let s = s.clone();
|
||||
rt.block_on(async move {
|
||||
let j = tokio::try_join! {
|
||||
task::spawn(task(s.clone())),
|
||||
task::spawn(task(s.clone())),
|
||||
task::spawn(task(s.clone())),
|
||||
task::spawn(task(s.clone())),
|
||||
task::spawn(task(s.clone())),
|
||||
task::spawn(task(s.clone()))
|
||||
};
|
||||
j.unwrap();
|
||||
g.bench_function("concurrent_multi", |b| {
|
||||
b.iter(|| {
|
||||
let s = s.clone();
|
||||
rt.block_on(async move {
|
||||
let j = tokio::try_join! {
|
||||
task::spawn(task(s.clone())),
|
||||
task::spawn(task(s.clone())),
|
||||
task::spawn(task(s.clone())),
|
||||
task::spawn(task(s.clone())),
|
||||
task::spawn(task(s.clone())),
|
||||
task::spawn(task(s.clone()))
|
||||
};
|
||||
j.unwrap();
|
||||
})
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn contended_concurrent_single(b: &mut Bencher) {
|
||||
let rt = tokio::runtime::Builder::new_current_thread()
|
||||
.build()
|
||||
.unwrap();
|
||||
fn contended_concurrent_single(g: &mut BenchmarkGroup<WallTime>) {
|
||||
let rt = single_rt();
|
||||
|
||||
let s = Arc::new(Semaphore::new(5));
|
||||
b.iter(|| {
|
||||
let s = s.clone();
|
||||
rt.block_on(async move {
|
||||
tokio::join! {
|
||||
task(s.clone()),
|
||||
task(s.clone()),
|
||||
task(s.clone()),
|
||||
task(s.clone()),
|
||||
task(s.clone()),
|
||||
task(s.clone())
|
||||
};
|
||||
g.bench_function("concurrent_single", |b| {
|
||||
b.iter(|| {
|
||||
let s = s.clone();
|
||||
rt.block_on(async move {
|
||||
tokio::join! {
|
||||
task(s.clone()),
|
||||
task(s.clone()),
|
||||
task(s.clone()),
|
||||
task(s.clone()),
|
||||
task(s.clone()),
|
||||
task(s.clone())
|
||||
};
|
||||
})
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
bencher::benchmark_group!(
|
||||
sync_semaphore,
|
||||
uncontended,
|
||||
uncontended_concurrent_multi,
|
||||
uncontended_concurrent_single,
|
||||
contended_concurrent_multi,
|
||||
contended_concurrent_single
|
||||
);
|
||||
fn bench_contention(c: &mut Criterion) {
|
||||
let mut group = c.benchmark_group("contention");
|
||||
contended_concurrent_multi(&mut group);
|
||||
contended_concurrent_single(&mut group);
|
||||
group.finish();
|
||||
}
|
||||
|
||||
bencher::benchmark_main!(sync_semaphore);
|
||||
fn bench_uncontented(c: &mut Criterion) {
|
||||
let mut group = c.benchmark_group("uncontented");
|
||||
uncontended(&mut group);
|
||||
uncontended_concurrent_multi(&mut group);
|
||||
uncontended_concurrent_single(&mut group);
|
||||
group.finish();
|
||||
}
|
||||
|
||||
criterion_group!(contention, bench_contention);
|
||||
criterion_group!(uncontented, bench_uncontented);
|
||||
|
||||
criterion_main!(contention, uncontented);
|
||||
|
||||
+41
-20
@@ -1,9 +1,11 @@
|
||||
use bencher::{black_box, Bencher};
|
||||
use rand::prelude::*;
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::{watch, Notify};
|
||||
|
||||
use criterion::measurement::WallTime;
|
||||
use criterion::{black_box, criterion_group, criterion_main, BenchmarkGroup, Criterion};
|
||||
|
||||
fn rt() -> tokio::runtime::Runtime {
|
||||
tokio::runtime::Builder::new_multi_thread()
|
||||
.worker_threads(6)
|
||||
@@ -15,7 +17,7 @@ fn do_work(rng: &mut impl RngCore) -> u32 {
|
||||
use std::fmt::Write;
|
||||
let mut message = String::new();
|
||||
for i in 1..=10 {
|
||||
let _ = write!(&mut message, " {i}={}", rng.gen::<f64>());
|
||||
let _ = write!(&mut message, " {i}={}", rng.random::<f64>());
|
||||
}
|
||||
message
|
||||
.as_bytes()
|
||||
@@ -24,16 +26,15 @@ fn do_work(rng: &mut impl RngCore) -> u32 {
|
||||
.fold(0, u32::wrapping_add)
|
||||
}
|
||||
|
||||
fn contention_resubscribe(b: &mut Bencher) {
|
||||
const NTASK: u64 = 1000;
|
||||
|
||||
fn contention_resubscribe<const N_TASKS: usize>(g: &mut BenchmarkGroup<WallTime>) {
|
||||
let rt = rt();
|
||||
let (snd, rcv) = watch::channel(0i32);
|
||||
let (snd, _) = watch::channel(0i32);
|
||||
let snd = Arc::new(snd);
|
||||
let wg = Arc::new((AtomicU64::new(0), Notify::new()));
|
||||
for n in 0..NTASK {
|
||||
let mut rcv = rcv.clone();
|
||||
for n in 0..N_TASKS {
|
||||
let mut rcv = snd.subscribe();
|
||||
let wg = wg.clone();
|
||||
let mut rng = rand::rngs::StdRng::seed_from_u64(n);
|
||||
let mut rng = rand::rngs::StdRng::seed_from_u64(n as u64);
|
||||
rt.spawn(async move {
|
||||
while rcv.changed().await.is_ok() {
|
||||
let _ = *rcv.borrow(); // contend on rwlock
|
||||
@@ -46,19 +47,39 @@ fn contention_resubscribe(b: &mut Bencher) {
|
||||
});
|
||||
}
|
||||
|
||||
b.iter(|| {
|
||||
rt.block_on(async {
|
||||
for _ in 0..100 {
|
||||
assert_eq!(wg.0.fetch_add(NTASK, Ordering::Relaxed), 0);
|
||||
let _ = snd.send(black_box(42));
|
||||
while wg.0.load(Ordering::Acquire) > 0 {
|
||||
wg.1.notified().await;
|
||||
const N_ITERS: usize = 100;
|
||||
g.bench_function(N_TASKS.to_string(), |b| {
|
||||
b.iter(|| {
|
||||
rt.block_on({
|
||||
let snd = snd.clone();
|
||||
let wg = wg.clone();
|
||||
async move {
|
||||
tokio::spawn(async move {
|
||||
for _ in 0..N_ITERS {
|
||||
assert_eq!(wg.0.fetch_add(N_TASKS as u64, Ordering::Relaxed), 0);
|
||||
let _ = snd.send(black_box(42));
|
||||
while wg.0.load(Ordering::Acquire) > 0 {
|
||||
wg.1.notified().await;
|
||||
}
|
||||
}
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
bencher::benchmark_group!(contention, contention_resubscribe);
|
||||
fn bench_contention_resubscribe(c: &mut Criterion) {
|
||||
let mut group = c.benchmark_group("contention_resubscribe");
|
||||
contention_resubscribe::<10>(&mut group);
|
||||
contention_resubscribe::<100>(&mut group);
|
||||
contention_resubscribe::<500>(&mut group);
|
||||
contention_resubscribe::<1000>(&mut group);
|
||||
group.finish();
|
||||
}
|
||||
|
||||
bencher::benchmark_main!(contention);
|
||||
criterion_group!(contention, bench_contention_resubscribe);
|
||||
|
||||
criterion_main!(contention);
|
||||
|
||||
+10
-11
@@ -2,24 +2,23 @@
|
||||
//! This essentially measure the time to enqueue a task in the local and remote
|
||||
//! case.
|
||||
|
||||
#[macro_use]
|
||||
extern crate bencher;
|
||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||
|
||||
use bencher::{black_box, Bencher};
|
||||
|
||||
fn time_now_current_thread(bench: &mut Bencher) {
|
||||
fn time_now_current_thread(c: &mut Criterion) {
|
||||
let rt = tokio::runtime::Builder::new_current_thread()
|
||||
.enable_time()
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
bench.iter(|| {
|
||||
rt.block_on(async {
|
||||
black_box(tokio::time::Instant::now());
|
||||
c.bench_function("time_now_current_thread", |b| {
|
||||
b.iter(|| {
|
||||
rt.block_on(async {
|
||||
black_box(tokio::time::Instant::now());
|
||||
})
|
||||
})
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
bencher::benchmark_group!(time_now, time_now_current_thread,);
|
||||
criterion_group!(time_now, time_now_current_thread);
|
||||
|
||||
bencher::benchmark_main!(time_now);
|
||||
criterion_main!(time_now);
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||
use tokio::{
|
||||
runtime::Runtime,
|
||||
time::{sleep, timeout},
|
||||
};
|
||||
|
||||
// a very quick async task, but might timeout
|
||||
async fn quick_job() -> usize {
|
||||
1
|
||||
}
|
||||
|
||||
fn build_run_time(workers: usize) -> Runtime {
|
||||
if workers == 1 {
|
||||
tokio::runtime::Builder::new_current_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
.unwrap()
|
||||
} else {
|
||||
tokio::runtime::Builder::new_multi_thread()
|
||||
.enable_all()
|
||||
.worker_threads(workers)
|
||||
.build()
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
fn single_thread_scheduler_timeout(c: &mut Criterion) {
|
||||
do_timeout_test(c, 1, "single_thread_timeout");
|
||||
}
|
||||
|
||||
fn multi_thread_scheduler_timeout(c: &mut Criterion) {
|
||||
do_timeout_test(c, 8, "multi_thread_timeout-8");
|
||||
}
|
||||
|
||||
fn do_timeout_test(c: &mut Criterion, workers: usize, name: &str) {
|
||||
let runtime = build_run_time(workers);
|
||||
c.bench_function(name, |b| {
|
||||
b.iter_custom(|iters| {
|
||||
let start = Instant::now();
|
||||
runtime.block_on(async {
|
||||
black_box(spawn_timeout_job(iters as usize, workers)).await;
|
||||
});
|
||||
start.elapsed()
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
async fn spawn_timeout_job(iters: usize, procs: usize) {
|
||||
let mut handles = Vec::with_capacity(procs);
|
||||
for _ in 0..procs {
|
||||
handles.push(tokio::spawn(async move {
|
||||
for _ in 0..iters / procs {
|
||||
let h = timeout(Duration::from_secs(1), quick_job());
|
||||
assert_eq!(black_box(h.await.unwrap()), 1);
|
||||
}
|
||||
}));
|
||||
}
|
||||
for handle in handles {
|
||||
handle.await.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
fn single_thread_scheduler_sleep(c: &mut Criterion) {
|
||||
do_sleep_test(c, 1, "single_thread_sleep");
|
||||
}
|
||||
|
||||
fn multi_thread_scheduler_sleep(c: &mut Criterion) {
|
||||
do_sleep_test(c, 8, "multi_thread_sleep-8");
|
||||
}
|
||||
|
||||
fn do_sleep_test(c: &mut Criterion, workers: usize, name: &str) {
|
||||
let runtime = build_run_time(workers);
|
||||
|
||||
c.bench_function(name, |b| {
|
||||
b.iter_custom(|iters| {
|
||||
let start = Instant::now();
|
||||
runtime.block_on(async {
|
||||
black_box(spawn_sleep_job(iters as usize, workers)).await;
|
||||
});
|
||||
start.elapsed()
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
async fn spawn_sleep_job(iters: usize, procs: usize) {
|
||||
let mut handles = Vec::with_capacity(procs);
|
||||
for _ in 0..procs {
|
||||
handles.push(tokio::spawn(async move {
|
||||
for _ in 0..iters / procs {
|
||||
let _h = black_box(sleep(Duration::from_secs(1)));
|
||||
}
|
||||
}));
|
||||
}
|
||||
for handle in handles {
|
||||
handle.await.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
criterion_group!(
|
||||
timeout_benchmark,
|
||||
single_thread_scheduler_timeout,
|
||||
multi_thread_scheduler_timeout,
|
||||
single_thread_scheduler_sleep,
|
||||
multi_thread_scheduler_sleep
|
||||
);
|
||||
|
||||
criterion_main!(timeout_benchmark);
|
||||
-121
@@ -1,121 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
USAGE="Publish a new release of a tokio crate
|
||||
|
||||
USAGE:
|
||||
$(basename "$0") [OPTIONS] [CRATE] [VERSION]
|
||||
|
||||
OPTIONS:
|
||||
-v, --verbose Use verbose Cargo output
|
||||
-d, --dry-run Perform a dry run (do not publish or tag the release)
|
||||
-h, --help Show this help text and exit"
|
||||
|
||||
DRY_RUN=""
|
||||
VERBOSE=""
|
||||
|
||||
err() {
|
||||
echo -e "\e[31m\e[1merror:\e[0m $@" 1>&2;
|
||||
}
|
||||
|
||||
status() {
|
||||
WIDTH=12
|
||||
printf "\e[32m\e[1m%${WIDTH}s\e[0m %s\n" "$1" "$2"
|
||||
}
|
||||
|
||||
verify() {
|
||||
status "Verifying" "if $CRATE v$VERSION can be released"
|
||||
ACTUAL=$(cargo pkgid | sed -n 's/.*#\(.*\)/\1/p')
|
||||
|
||||
if [ "$ACTUAL" != "$VERSION" ]; then
|
||||
err "expected to release version $VERSION, but Cargo.toml contained $ACTUAL"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if git tag -l | grep -Fxq "$TAG" ; then
|
||||
err "git tag \`$TAG\` already exists"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
PATH_DEPS=$(grep -F "path = \"" Cargo.toml | sed -e 's/^/ /')
|
||||
if [ -n "$PATH_DEPS" ]; then
|
||||
err "crate \`$CRATE\` contained path dependencies:\n$PATH_DEPS"
|
||||
echo "path dependencies must be removed prior to release"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
release() {
|
||||
status "Releasing" "$CRATE v$VERSION"
|
||||
cargo package $VERBOSE
|
||||
cargo publish $VERBOSE $DRY_RUN
|
||||
|
||||
status "Tagging" "$TAG"
|
||||
if [ -n "$DRY_RUN" ]; then
|
||||
echo "# git tag $TAG && git push --tags"
|
||||
else
|
||||
git tag "$TAG" && git push --tags
|
||||
fi
|
||||
}
|
||||
|
||||
while [[ $# -gt 0 ]]
|
||||
do
|
||||
|
||||
case "$1" in
|
||||
-h|--help)
|
||||
echo "$USAGE"
|
||||
exit 0
|
||||
;;
|
||||
-v|--verbose)
|
||||
VERBOSE="--verbose"
|
||||
set +x
|
||||
shift
|
||||
;;
|
||||
-d|--dry-run)
|
||||
DRY_RUN="--dry-run"
|
||||
shift
|
||||
;;
|
||||
-*)
|
||||
err "unknown flag \"$1\""
|
||||
echo "$USAGE"
|
||||
exit 1
|
||||
;;
|
||||
*) # crate or version
|
||||
if [ -z "$CRATE" ]; then
|
||||
CRATE="$1"
|
||||
elif [ -z "$VERSION" ]; then
|
||||
VERSION="$1"
|
||||
else
|
||||
err "unknown positional argument \"$1\""
|
||||
echo "$USAGE"
|
||||
exit 1
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
# set -- "${POSITIONAL[@]}"
|
||||
|
||||
if [ -z "$VERSION" ]; then
|
||||
err "no version specified!"
|
||||
HELP=1
|
||||
fi
|
||||
|
||||
if [ -n "$CRATE" ]; then
|
||||
TAG="$CRATE-$VERSION"
|
||||
else
|
||||
err "no crate specified!"
|
||||
HELP=1
|
||||
fi
|
||||
|
||||
if [ -n "$HELP" ]; then
|
||||
echo "$USAGE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -d "$CRATE" ]; then
|
||||
(cd "$CRATE" && verify && release )
|
||||
else
|
||||
err "no such crate \"$CRATE\""
|
||||
exit 1
|
||||
fi
|
||||
|
||||
-118
@@ -1,118 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
USAGE="Update links to docs.rs in a tokio crate
|
||||
|
||||
USAGE:
|
||||
$(basename "$0") [OPTIONS] [CRATE] [VERSION]
|
||||
|
||||
OPTIONS:
|
||||
-d, --dry-run Perform a dry run (do not modify any file)
|
||||
-h, --help Show this help text and exit"
|
||||
|
||||
err() {
|
||||
echo -e "\e[31m\e[1merror:\e[0m $@" 1>&2;
|
||||
}
|
||||
|
||||
status() {
|
||||
WIDTH=12
|
||||
printf "\e[32m\e[1m%${WIDTH}s\e[0m %s\n" "$1" "$2"
|
||||
}
|
||||
|
||||
c1grep() { grep "$@" || test $? = 1; }
|
||||
|
||||
update_versions_in_doc() {
|
||||
# Print what is being/would be done
|
||||
if [ -n "$DRY_RUN" ]; then
|
||||
local MSG="Would change:"
|
||||
else
|
||||
local MSG="Updating:"
|
||||
fi
|
||||
git grep -lr "docs.rs/$CRATE/" \
|
||||
| xargs sed --quiet \
|
||||
-E "s|docs.rs/$CRATE/[0-9.]+|docs.rs/$CRATE/$VERSION|gp" \
|
||||
| sed -e "s/^/$MSG /"
|
||||
|
||||
# Apply changes if not in dry run
|
||||
if [ -z "$DRY_RUN" ]; then
|
||||
git grep -lr "docs.rs/$CRATE/" \
|
||||
| xargs sed -i \
|
||||
-E "s|docs.rs/$CRATE/[0-9.]+|docs.rs/$CRATE/$VERSION|g"
|
||||
fi
|
||||
}
|
||||
|
||||
update() {
|
||||
update_versions_in_doc
|
||||
}
|
||||
|
||||
show_outdated() {
|
||||
OUTDATED=$(git grep -rn "docs.rs/$CRATE/" \
|
||||
| c1grep -v "$VERSION" \
|
||||
| sed -e 's/^/ - /')
|
||||
if [[ -n "$OUTDATED" ]]; then
|
||||
echo "Found the following links to docs.rs with an outdated version:"
|
||||
echo "$OUTDATED"
|
||||
echo
|
||||
else
|
||||
echo "Nothing to do."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
while [[ $# -gt 0 ]]
|
||||
do
|
||||
|
||||
case "$1" in
|
||||
-h|--help)
|
||||
echo "$USAGE"
|
||||
exit 0
|
||||
;;
|
||||
-d|--dry-run)
|
||||
DRY_RUN="--dry-run"
|
||||
shift
|
||||
;;
|
||||
-*)
|
||||
err "unknown flag \"$1\""
|
||||
echo "$USAGE"
|
||||
exit 1
|
||||
;;
|
||||
*) # crate or version
|
||||
if [ -z "$CRATE" ]; then
|
||||
CRATE="$1"
|
||||
elif [ -z "$VERSION" ]; then
|
||||
VERSION="$1"
|
||||
else
|
||||
err "unknown positional argument \"$1\""
|
||||
echo "$USAGE"
|
||||
exit 1
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
# set -- "${POSITIONAL[@]}"
|
||||
|
||||
if [ -z "$VERSION" ]; then
|
||||
err "no version specified!"
|
||||
HELP=1
|
||||
fi
|
||||
|
||||
if [ -n "$CRATE" ]; then
|
||||
TAG="$CRATE-$VERSION"
|
||||
else
|
||||
err "no crate specified!"
|
||||
HELP=1
|
||||
fi
|
||||
|
||||
if [ -n "$HELP" ]; then
|
||||
echo "$USAGE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -d "$CRATE" ]; then
|
||||
# Does not cd in order to update everywhere
|
||||
show_outdated && update
|
||||
else
|
||||
err "no such crate \"$CRATE\""
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
# https://embarkstudios.github.io/cargo-deny/cli/init.html
|
||||
|
||||
[graph]
|
||||
all-features = true
|
||||
|
||||
[licenses]
|
||||
allow = [
|
||||
"MIT",
|
||||
"Apache-2.0",
|
||||
]
|
||||
exceptions = [
|
||||
{ allow = ["Unicode-3.0", "Unicode-DFS-2016"], crate = "unicode-ident" },
|
||||
]
|
||||
|
||||
[bans]
|
||||
multiple-versions = "allow"
|
||||
wildcards = "deny"
|
||||
|
||||
[sources]
|
||||
unknown-registry = "deny"
|
||||
unknown-git = "deny"
|
||||
+15
-8
@@ -3,6 +3,7 @@ name = "examples"
|
||||
version = "0.0.0"
|
||||
publish = false
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
|
||||
# If you copy one of the examples into a new project, you should be using
|
||||
# [dependencies] instead, and delete the **path**.
|
||||
@@ -22,27 +23,30 @@ serde_json = "1.0"
|
||||
httparse = "1.0"
|
||||
httpdate = "1.0"
|
||||
once_cell = "1.5.2"
|
||||
rand = "0.8.3"
|
||||
|
||||
[target.'cfg(windows)'.dev-dependencies.windows-sys]
|
||||
version = "0.48"
|
||||
version = "0.59"
|
||||
|
||||
[[example]]
|
||||
name = "chat"
|
||||
path = "chat.rs"
|
||||
|
||||
[[example]]
|
||||
name = "connect"
|
||||
path = "connect.rs"
|
||||
name = "connect-tcp"
|
||||
path = "connect-tcp.rs"
|
||||
|
||||
[[example]]
|
||||
name = "connect-udp"
|
||||
path = "connect-udp.rs"
|
||||
|
||||
[[example]]
|
||||
name = "echo-tcp"
|
||||
path = "echo-tcp.rs"
|
||||
|
||||
[[example]]
|
||||
name = "echo-udp"
|
||||
path = "echo-udp.rs"
|
||||
|
||||
[[example]]
|
||||
name = "echo"
|
||||
path = "echo.rs"
|
||||
|
||||
[[example]]
|
||||
name = "hello_world"
|
||||
path = "hello_world.rs"
|
||||
@@ -94,3 +98,6 @@ path = "named-pipe-multi-client.rs"
|
||||
[[example]]
|
||||
name = "dump"
|
||||
path = "dump.rs"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ cargo run --example $name
|
||||
```
|
||||
|
||||
A good starting point for the examples would be [`hello_world`](hello_world.rs)
|
||||
and [`echo`](echo.rs). Additionally [the tokio website][tokioweb] contains
|
||||
and [`echo-tcp`](echo-tcp.rs). Additionally [the tokio website][tokioweb] contains
|
||||
additional guides for some of the examples.
|
||||
|
||||
For a larger "real world" example, see the [`mini-redis`][redis] repository.
|
||||
|
||||
+3
-3
@@ -193,7 +193,7 @@ async fn process(
|
||||
// A client has connected, let's let everyone know.
|
||||
{
|
||||
let mut state = state.lock().await;
|
||||
let msg = format!("{} has joined the chat", username);
|
||||
let msg = format!("{username} has joined the chat");
|
||||
tracing::info!("{}", msg);
|
||||
state.broadcast(addr, &msg).await;
|
||||
}
|
||||
@@ -210,7 +210,7 @@ async fn process(
|
||||
// broadcast this message to the other users.
|
||||
Some(Ok(msg)) => {
|
||||
let mut state = state.lock().await;
|
||||
let msg = format!("{}: {}", username, msg);
|
||||
let msg = format!("{username}: {msg}");
|
||||
|
||||
state.broadcast(addr, &msg).await;
|
||||
}
|
||||
@@ -234,7 +234,7 @@ async fn process(
|
||||
let mut state = state.lock().await;
|
||||
state.peers.remove(&addr);
|
||||
|
||||
let msg = format!("{} has left the chat", username);
|
||||
let msg = format!("{username} has left the chat");
|
||||
tracing::info!("{}", msg);
|
||||
state.broadcast(addr, &msg).await;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
//! An example of hooking up stdin/stdout to a TCP stream.
|
||||
//!
|
||||
//! This example will connect to a socket address specified in the argument list
|
||||
//! and then forward all data read on stdin to the server, printing out all data
|
||||
//! received on stdout. Each line entered on stdin will be translated to a TCP
|
||||
//! packet which is then sent to the remote address.
|
||||
//!
|
||||
//! Note that this is not currently optimized for performance, especially
|
||||
//! around buffer management. Rather it's intended to show an example of
|
||||
//! working with a client.
|
||||
//!
|
||||
//! This example can be quite useful when interacting with the other examples in
|
||||
//! this repository! Many of them recommend running this as a simple "hook up
|
||||
//! stdin/stdout to a server" to get up and running.
|
||||
|
||||
#![warn(rust_2018_idioms)]
|
||||
|
||||
use tokio::io::{stdin, stdout};
|
||||
use tokio::net::TcpStream;
|
||||
use tokio_util::codec::{BytesCodec, FramedRead, FramedWrite};
|
||||
|
||||
use bytes::Bytes;
|
||||
use futures::{future, Sink, SinkExt, Stream, StreamExt};
|
||||
use std::env;
|
||||
use std::error::Error;
|
||||
use std::net::SocketAddr;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn Error>> {
|
||||
// Parse what address we're going to connect to
|
||||
let args = env::args().skip(1).collect::<Vec<_>>();
|
||||
let addr = args
|
||||
.first()
|
||||
.ok_or("this program requires at least one argument")?;
|
||||
let addr = addr.parse::<SocketAddr>()?;
|
||||
|
||||
let stdin = FramedRead::new(stdin(), BytesCodec::new());
|
||||
let stdin = stdin.map(|i| i.map(|bytes| bytes.freeze()));
|
||||
let stdout = FramedWrite::new(stdout(), BytesCodec::new());
|
||||
|
||||
connect(&addr, stdin, stdout).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn connect(
|
||||
addr: &SocketAddr,
|
||||
mut stdin: impl Stream<Item = Result<Bytes, std::io::Error>> + Unpin,
|
||||
mut stdout: impl Sink<Bytes, Error = std::io::Error> + Unpin,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
let mut stream = TcpStream::connect(addr).await?;
|
||||
let (r, w) = stream.split();
|
||||
let mut sink = FramedWrite::new(w, BytesCodec::new());
|
||||
// filter map Result<BytesMut, Error> stream into just a Bytes stream to match stdout Sink
|
||||
// on the event of an Error, log the error and end the stream
|
||||
let mut stream = FramedRead::new(r, BytesCodec::new())
|
||||
.filter_map(|i| match i {
|
||||
//BytesMut into Bytes
|
||||
Ok(i) => future::ready(Some(i.freeze())),
|
||||
Err(e) => {
|
||||
println!("failed to read from socket; error={e}");
|
||||
future::ready(None)
|
||||
}
|
||||
})
|
||||
.map(Ok);
|
||||
|
||||
match future::join(sink.send_all(&mut stdin), stdout.send_all(&mut stream)).await {
|
||||
(Err(e), _) | (_, Err(e)) => Err(e.into()),
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
//! An example of hooking up stdin/stdout to a UDP stream.
|
||||
//!
|
||||
//! This example will connect to a socket address specified in the argument list
|
||||
//! and then forward all data read on stdin to the server, printing out all data
|
||||
//! received on stdout. Each line entered on stdin will be translated to a UDP
|
||||
//! packet which is then sent to the remote address.
|
||||
//!
|
||||
//! Note that this is not currently optimized for performance, especially
|
||||
//! around buffer management. Rather it's intended to show an example of
|
||||
//! working with a client.
|
||||
//!
|
||||
//! This example can be quite useful when interacting with the other examples in
|
||||
//! this repository! Many of them recommend running this as a simple "hook up
|
||||
//! stdin/stdout to a server" to get up and running.
|
||||
|
||||
#![warn(rust_2018_idioms)]
|
||||
|
||||
use tokio::io::{stdin, stdout};
|
||||
use tokio::net::UdpSocket;
|
||||
use tokio_util::codec::{BytesCodec, FramedRead, FramedWrite};
|
||||
|
||||
use bytes::Bytes;
|
||||
use futures::{Sink, SinkExt, Stream, StreamExt};
|
||||
use std::env;
|
||||
use std::error::Error;
|
||||
use std::net::SocketAddr;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn Error>> {
|
||||
// Parse what address we're going to connect to
|
||||
let args = env::args().skip(1).collect::<Vec<_>>();
|
||||
let addr = args
|
||||
.first()
|
||||
.ok_or("this program requires at least one argument")?;
|
||||
let addr = addr.parse::<SocketAddr>()?;
|
||||
|
||||
let stdin = FramedRead::new(stdin(), BytesCodec::new());
|
||||
let stdin = stdin.map(|i| i.map(|bytes| bytes.freeze()));
|
||||
let stdout = FramedWrite::new(stdout(), BytesCodec::new());
|
||||
|
||||
connect(&addr, stdin, stdout).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn connect(
|
||||
addr: &SocketAddr,
|
||||
stdin: impl Stream<Item = Result<Bytes, std::io::Error>> + Unpin,
|
||||
stdout: impl Sink<Bytes, Error = std::io::Error> + Unpin,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
// We'll bind our UDP socket to a local IP/port, but for now we
|
||||
// basically let the OS pick both of those.
|
||||
let bind_addr = if addr.ip().is_ipv4() {
|
||||
"0.0.0.0:0"
|
||||
} else {
|
||||
"[::]:0"
|
||||
};
|
||||
|
||||
let socket = UdpSocket::bind(&bind_addr).await?;
|
||||
socket.connect(addr).await?;
|
||||
|
||||
tokio::try_join!(send(stdin, &socket), recv(stdout, &socket))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn send(
|
||||
mut stdin: impl Stream<Item = Result<Bytes, std::io::Error>> + Unpin,
|
||||
writer: &UdpSocket,
|
||||
) -> Result<(), std::io::Error> {
|
||||
while let Some(item) = stdin.next().await {
|
||||
let buf = item?;
|
||||
writer.send(&buf[..]).await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn recv(
|
||||
mut stdout: impl Sink<Bytes, Error = std::io::Error> + Unpin,
|
||||
reader: &UdpSocket,
|
||||
) -> Result<(), std::io::Error> {
|
||||
loop {
|
||||
let mut buf = vec![0; 1024];
|
||||
let n = reader.recv(&mut buf[..]).await?;
|
||||
|
||||
if n > 0 {
|
||||
stdout.send(Bytes::from(buf)).await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,147 +0,0 @@
|
||||
//! An example of hooking up stdin/stdout to either a TCP or UDP stream.
|
||||
//!
|
||||
//! This example will connect to a socket address specified in the argument list
|
||||
//! and then forward all data read on stdin to the server, printing out all data
|
||||
//! received on stdout. An optional `--udp` argument can be passed to specify
|
||||
//! that the connection should be made over UDP instead of TCP, translating each
|
||||
//! line entered on stdin to a UDP packet to be sent to the remote address.
|
||||
//!
|
||||
//! Note that this is not currently optimized for performance, especially
|
||||
//! around buffer management. Rather it's intended to show an example of
|
||||
//! working with a client.
|
||||
//!
|
||||
//! This example can be quite useful when interacting with the other examples in
|
||||
//! this repository! Many of them recommend running this as a simple "hook up
|
||||
//! stdin/stdout to a server" to get up and running.
|
||||
|
||||
#![warn(rust_2018_idioms)]
|
||||
|
||||
use futures::StreamExt;
|
||||
use tokio::io;
|
||||
use tokio_util::codec::{BytesCodec, FramedRead, FramedWrite};
|
||||
|
||||
use std::env;
|
||||
use std::error::Error;
|
||||
use std::net::SocketAddr;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn Error>> {
|
||||
// Determine if we're going to run in TCP or UDP mode
|
||||
let mut args = env::args().skip(1).collect::<Vec<_>>();
|
||||
let tcp = match args.iter().position(|a| a == "--udp") {
|
||||
Some(i) => {
|
||||
args.remove(i);
|
||||
false
|
||||
}
|
||||
None => true,
|
||||
};
|
||||
|
||||
// Parse what address we're going to connect to
|
||||
let addr = args
|
||||
.first()
|
||||
.ok_or("this program requires at least one argument")?;
|
||||
let addr = addr.parse::<SocketAddr>()?;
|
||||
|
||||
let stdin = FramedRead::new(io::stdin(), BytesCodec::new());
|
||||
let stdin = stdin.map(|i| i.map(|bytes| bytes.freeze()));
|
||||
let stdout = FramedWrite::new(io::stdout(), BytesCodec::new());
|
||||
|
||||
if tcp {
|
||||
tcp::connect(&addr, stdin, stdout).await?;
|
||||
} else {
|
||||
udp::connect(&addr, stdin, stdout).await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
mod tcp {
|
||||
use bytes::Bytes;
|
||||
use futures::{future, Sink, SinkExt, Stream, StreamExt};
|
||||
use std::{error::Error, io, net::SocketAddr};
|
||||
use tokio::net::TcpStream;
|
||||
use tokio_util::codec::{BytesCodec, FramedRead, FramedWrite};
|
||||
|
||||
pub async fn connect(
|
||||
addr: &SocketAddr,
|
||||
mut stdin: impl Stream<Item = Result<Bytes, io::Error>> + Unpin,
|
||||
mut stdout: impl Sink<Bytes, Error = io::Error> + Unpin,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
let mut stream = TcpStream::connect(addr).await?;
|
||||
let (r, w) = stream.split();
|
||||
let mut sink = FramedWrite::new(w, BytesCodec::new());
|
||||
// filter map Result<BytesMut, Error> stream into just a Bytes stream to match stdout Sink
|
||||
// on the event of an Error, log the error and end the stream
|
||||
let mut stream = FramedRead::new(r, BytesCodec::new())
|
||||
.filter_map(|i| match i {
|
||||
//BytesMut into Bytes
|
||||
Ok(i) => future::ready(Some(i.freeze())),
|
||||
Err(e) => {
|
||||
println!("failed to read from socket; error={}", e);
|
||||
future::ready(None)
|
||||
}
|
||||
})
|
||||
.map(Ok);
|
||||
|
||||
match future::join(sink.send_all(&mut stdin), stdout.send_all(&mut stream)).await {
|
||||
(Err(e), _) | (_, Err(e)) => Err(e.into()),
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod udp {
|
||||
use bytes::Bytes;
|
||||
use futures::{Sink, SinkExt, Stream, StreamExt};
|
||||
use std::error::Error;
|
||||
use std::io;
|
||||
use std::net::SocketAddr;
|
||||
use tokio::net::UdpSocket;
|
||||
|
||||
pub async fn connect(
|
||||
addr: &SocketAddr,
|
||||
stdin: impl Stream<Item = Result<Bytes, io::Error>> + Unpin,
|
||||
stdout: impl Sink<Bytes, Error = io::Error> + Unpin,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
// We'll bind our UDP socket to a local IP/port, but for now we
|
||||
// basically let the OS pick both of those.
|
||||
let bind_addr = if addr.ip().is_ipv4() {
|
||||
"0.0.0.0:0"
|
||||
} else {
|
||||
"[::]:0"
|
||||
};
|
||||
|
||||
let socket = UdpSocket::bind(&bind_addr).await?;
|
||||
socket.connect(addr).await?;
|
||||
|
||||
tokio::try_join!(send(stdin, &socket), recv(stdout, &socket))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn send(
|
||||
mut stdin: impl Stream<Item = Result<Bytes, io::Error>> + Unpin,
|
||||
writer: &UdpSocket,
|
||||
) -> Result<(), io::Error> {
|
||||
while let Some(item) = stdin.next().await {
|
||||
let buf = item?;
|
||||
writer.send(&buf[..]).await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn recv(
|
||||
mut stdout: impl Sink<Bytes, Error = io::Error> + Unpin,
|
||||
reader: &UdpSocket,
|
||||
) -> Result<(), io::Error> {
|
||||
loop {
|
||||
let mut buf = vec![0; 1024];
|
||||
let n = reader.recv(&mut buf[..]).await?;
|
||||
|
||||
if n > 0 {
|
||||
stdout.send(Bytes::from(buf)).await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@ fn main() {
|
||||
|
||||
// Without the `HandleExt.wrap()` there would be a panic because there is
|
||||
// no timer running, since it would be referencing runtime r1.
|
||||
let _ = rt1.block_on(rt2.wrap(async move {
|
||||
rt1.block_on(rt2.wrap(async move {
|
||||
let listener = TcpListener::bind("0.0.0.0:0").await.unwrap();
|
||||
println!("addr: {:?}", listener.local_addr());
|
||||
tx.send(()).unwrap();
|
||||
|
||||
+3
-2
@@ -47,9 +47,10 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// capture a dump, and print each trace
|
||||
println!("{:-<80}", "");
|
||||
if let Ok(dump) = timeout(Duration::from_secs(2), handle.dump()).await {
|
||||
for (i, task) in dump.tasks().iter().enumerate() {
|
||||
for task in dump.tasks().iter() {
|
||||
let id = task.id();
|
||||
let trace = task.trace();
|
||||
println!("TASK {i}:");
|
||||
println!("TASK {id}:");
|
||||
println!("{trace}\n");
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -9,13 +9,13 @@
|
||||
//!
|
||||
//! To see this server in action, you can run this in one terminal:
|
||||
//!
|
||||
//! cargo run --example echo
|
||||
//! cargo run --example echo-tcp
|
||||
//!
|
||||
//! and in another terminal you can run:
|
||||
//!
|
||||
//! cargo run --example connect 127.0.0.1:8080
|
||||
//! cargo run --example connect-tcp 127.0.0.1:8080
|
||||
//!
|
||||
//! Each line you type in to the `connect` terminal should be echo'd back to
|
||||
//! Each line you type in to the `connect-tcp` terminal should be echo'd back to
|
||||
//! you! If you open up multiple terminals running the `connect` example you
|
||||
//! should be able to see them all make progress simultaneously.
|
||||
|
||||
@@ -40,7 +40,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
||||
// connections. This TCP listener is bound to the address we determined
|
||||
// above and must be associated with an event loop.
|
||||
let listener = TcpListener::bind(&addr).await?;
|
||||
println!("Listening on: {}", addr);
|
||||
println!("Listening on: {addr}");
|
||||
|
||||
loop {
|
||||
// Asynchronously wait for an inbound socket.
|
||||
@@ -6,9 +6,9 @@
|
||||
//!
|
||||
//! and in another terminal you can run:
|
||||
//!
|
||||
//! cargo run --example connect -- --udp 127.0.0.1:8080
|
||||
//! cargo run --example connect-udp 127.0.0.1:8080
|
||||
//!
|
||||
//! Each line you type in to the `nc` terminal should be echo'd back to you!
|
||||
//! Each line you type in to the `connect-udp` terminal should be echo'd back to you!
|
||||
|
||||
#![warn(rust_2018_idioms)]
|
||||
|
||||
@@ -38,7 +38,7 @@ impl Server {
|
||||
if let Some((size, peer)) = to_send {
|
||||
let amt = socket.send_to(&buf[..size], &peer).await?;
|
||||
|
||||
println!("Echoed {}/{} bytes to {}", amt, size, peer);
|
||||
println!("Echoed {amt}/{size} bytes to {peer}");
|
||||
}
|
||||
|
||||
// If we're here then `to_send` is `None`, so we take a look for the
|
||||
|
||||
@@ -13,9 +13,9 @@
|
||||
//!
|
||||
//! and in another terminal you can run:
|
||||
//!
|
||||
//! cargo run --example connect 127.0.0.1:8080
|
||||
//! cargo run --example connect-tcp 127.0.0.1:8080
|
||||
//!
|
||||
//! Each line you type in to the `connect` terminal should be written to terminal!
|
||||
//! Each line you type in to the `connect-tcp` terminal should be written to terminal!
|
||||
//!
|
||||
//! Minimal js example:
|
||||
//!
|
||||
@@ -75,7 +75,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// to our event loop. After the socket's created we inform that we're ready
|
||||
// to go and start accepting connections.
|
||||
let listener = TcpListener::bind(&addr).await?;
|
||||
println!("Listening on: {}", addr);
|
||||
println!("Listening on: {addr}");
|
||||
|
||||
loop {
|
||||
// Asynchronously wait for an inbound socket.
|
||||
@@ -96,8 +96,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// The stream will return None once the client disconnects.
|
||||
while let Some(message) = framed.next().await {
|
||||
match message {
|
||||
Ok(bytes) => println!("bytes: {:?}", bytes),
|
||||
Err(err) => println!("Socket closed with error: {:?}", err),
|
||||
Ok(bytes) => println!("bytes: {bytes:?}"),
|
||||
Err(err) => println!("Socket closed with error: {err:?}"),
|
||||
}
|
||||
}
|
||||
println!("Socket received FIN packet and closed connection");
|
||||
|
||||
+5
-5
@@ -11,11 +11,11 @@
|
||||
//!
|
||||
//! This in another terminal
|
||||
//!
|
||||
//! cargo run --example echo
|
||||
//! cargo run --example echo-tcp
|
||||
//!
|
||||
//! And finally this in another terminal
|
||||
//!
|
||||
//! cargo run --example connect 127.0.0.1:8081
|
||||
//! cargo run --example connect-tcp 127.0.0.1:8081
|
||||
//!
|
||||
//! This final terminal will connect to our proxy, which will in turn connect to
|
||||
//! the echo server, and you'll be able to see data flowing between them.
|
||||
@@ -38,8 +38,8 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
||||
.nth(2)
|
||||
.unwrap_or_else(|| "127.0.0.1:8080".to_string());
|
||||
|
||||
println!("Listening on: {}", listen_addr);
|
||||
println!("Proxying to: {}", server_addr);
|
||||
println!("Listening on: {listen_addr}");
|
||||
println!("Proxying to: {server_addr}");
|
||||
|
||||
let listener = TcpListener::bind(listen_addr).await?;
|
||||
|
||||
@@ -50,7 +50,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
||||
copy_bidirectional(&mut inbound, &mut outbound)
|
||||
.map(|r| {
|
||||
if let Err(e) = r {
|
||||
println!("Failed to transfer; error={}", e);
|
||||
println!("Failed to transfer; error={e}");
|
||||
}
|
||||
})
|
||||
.await
|
||||
|
||||
+11
-11
@@ -12,9 +12,9 @@
|
||||
//!
|
||||
//! and next in another windows run:
|
||||
//!
|
||||
//! cargo run --example connect 127.0.0.1:8080
|
||||
//! cargo run --example connect-tcp 127.0.0.1:8080
|
||||
//!
|
||||
//! In the `connect` window you can type in commands where when you hit enter
|
||||
//! In the `connect-tcp` window you can type in commands where when you hit enter
|
||||
//! you'll get a response from the server for that command. An example session
|
||||
//! is:
|
||||
//!
|
||||
@@ -90,7 +90,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
||||
.unwrap_or_else(|| "127.0.0.1:8080".to_string());
|
||||
|
||||
let listener = TcpListener::bind(&addr).await?;
|
||||
println!("Listening on: {}", addr);
|
||||
println!("Listening on: {addr}");
|
||||
|
||||
// Create the shared state of this server that will be shared amongst all
|
||||
// clients. We populate the initial database and then create the `Database`
|
||||
@@ -131,11 +131,11 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
||||
let response = response.serialize();
|
||||
|
||||
if let Err(e) = lines.send(response.as_str()).await {
|
||||
println!("error on sending response; error = {:?}", e);
|
||||
println!("error on sending response; error = {e:?}");
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
println!("error on decoding from socket; error = {:?}", e);
|
||||
println!("error on decoding from socket; error = {e:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -143,7 +143,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
||||
// The connection will be closed at this point as `lines.next()` has returned `None`.
|
||||
});
|
||||
}
|
||||
Err(e) => println!("error accepting socket; error = {:?}", e),
|
||||
Err(e) => println!("error accepting socket; error = {e:?}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -162,7 +162,7 @@ fn handle_request(line: &str, db: &Arc<Database>) -> Response {
|
||||
value: value.clone(),
|
||||
},
|
||||
None => Response::Error {
|
||||
msg: format!("no key {}", key),
|
||||
msg: format!("no key {key}"),
|
||||
},
|
||||
},
|
||||
Request::Set { key, value } => {
|
||||
@@ -203,7 +203,7 @@ impl Request {
|
||||
value: value.to_string(),
|
||||
})
|
||||
}
|
||||
Some(cmd) => Err(format!("unknown command: {}", cmd)),
|
||||
Some(cmd) => Err(format!("unknown command: {cmd}")),
|
||||
None => Err("empty input".into()),
|
||||
}
|
||||
}
|
||||
@@ -212,13 +212,13 @@ impl Request {
|
||||
impl Response {
|
||||
fn serialize(&self) -> String {
|
||||
match *self {
|
||||
Response::Value { ref key, ref value } => format!("{} = {}", key, value),
|
||||
Response::Value { ref key, ref value } => format!("{key} = {value}"),
|
||||
Response::Set {
|
||||
ref key,
|
||||
ref value,
|
||||
ref previous,
|
||||
} => format!("set {} = `{}`, previous: {:?}", key, value, previous),
|
||||
Response::Error { ref msg } => format!("error: {}", msg),
|
||||
} => format!("set {key} = `{value}`, previous: {previous:?}"),
|
||||
Response::Error { ref msg } => format!("error: {msg}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+11
-19
@@ -31,13 +31,13 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
||||
.nth(1)
|
||||
.unwrap_or_else(|| "127.0.0.1:8080".to_string());
|
||||
let server = TcpListener::bind(&addr).await?;
|
||||
println!("Listening on: {}", addr);
|
||||
println!("Listening on: {addr}");
|
||||
|
||||
loop {
|
||||
let (stream, _) = server.accept().await?;
|
||||
tokio::spawn(async move {
|
||||
if let Err(e) = process(stream).await {
|
||||
println!("failed to process connection; error = {}", e);
|
||||
println!("failed to process connection; error = {e}");
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -82,9 +82,7 @@ async fn respond(req: Request<()>) -> Result<Response<String>, Box<dyn Error>> {
|
||||
String::new()
|
||||
}
|
||||
};
|
||||
let response = response
|
||||
.body(body)
|
||||
.map_err(|err| io::Error::new(io::ErrorKind::Other, err))?;
|
||||
let response = response.body(body).map_err(io::Error::other)?;
|
||||
|
||||
Ok(response)
|
||||
}
|
||||
@@ -159,8 +157,8 @@ impl Decoder for Http {
|
||||
let mut parsed_headers = [httparse::EMPTY_HEADER; 16];
|
||||
let mut r = httparse::Request::new(&mut parsed_headers);
|
||||
let status = r.parse(src).map_err(|e| {
|
||||
let msg = format!("failed to parse http request: {:?}", e);
|
||||
io::Error::new(io::ErrorKind::Other, msg)
|
||||
let msg = format!("failed to parse http request: {e:?}");
|
||||
io::Error::other(msg)
|
||||
})?;
|
||||
|
||||
let amt = match status {
|
||||
@@ -180,8 +178,7 @@ impl Decoder for Http {
|
||||
headers[i] = Some((k, v));
|
||||
}
|
||||
|
||||
let method = http::Method::try_from(r.method.unwrap())
|
||||
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
|
||||
let method = http::Method::try_from(r.method.unwrap()).map_err(io::Error::other)?;
|
||||
|
||||
(
|
||||
method,
|
||||
@@ -191,10 +188,7 @@ impl Decoder for Http {
|
||||
)
|
||||
};
|
||||
if version != 1 {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
"only HTTP/1.1 accepted",
|
||||
));
|
||||
return Err(io::Error::other("only HTTP/1.1 accepted"));
|
||||
}
|
||||
let data = src.split_to(amt).freeze();
|
||||
let mut ret = Request::builder();
|
||||
@@ -209,13 +203,11 @@ impl Decoder for Http {
|
||||
None => break,
|
||||
};
|
||||
let value = HeaderValue::from_bytes(data.slice(v.0..v.1).as_ref())
|
||||
.map_err(|_| io::Error::new(io::ErrorKind::Other, "header decode error"))?;
|
||||
.map_err(|_| io::Error::other("header decode error"))?;
|
||||
ret = ret.header(&data[k.0..k.1], value);
|
||||
}
|
||||
|
||||
let req = ret
|
||||
.body(())
|
||||
.map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
|
||||
let req = ret.body(()).map_err(io::Error::other)?;
|
||||
Ok(Some(req))
|
||||
}
|
||||
}
|
||||
@@ -259,11 +251,11 @@ mod date {
|
||||
unix_date: u64,
|
||||
}
|
||||
|
||||
thread_local!(static LAST: RefCell<LastRenderedNow> = RefCell::new(LastRenderedNow {
|
||||
thread_local!(static LAST: RefCell<LastRenderedNow> = const { RefCell::new(LastRenderedNow {
|
||||
bytes: [0; 128],
|
||||
amt: 0,
|
||||
unix_date: 0,
|
||||
}));
|
||||
}) });
|
||||
|
||||
impl fmt::Display for Now {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
|
||||
@@ -46,7 +46,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
||||
|
||||
// Run both futures simultaneously of `a` and `b` sending messages back and forth.
|
||||
match tokio::try_join!(a, b) {
|
||||
Err(e) => println!("an error occurred; error = {:?}", e),
|
||||
Err(e) => println!("an error occurred; error = {e:?}"),
|
||||
_ => println!("done!"),
|
||||
}
|
||||
|
||||
|
||||
+12
-2
@@ -1,6 +1,15 @@
|
||||
[build]
|
||||
# TODO: unfreeze toolchain
|
||||
# error[E0557]: feature has been removed
|
||||
# --> /opt/buildhome/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/lock_api-0.4.13/src/lib.rs:89:29
|
||||
# |
|
||||
# 89 | #![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||
# | ^^^^^^^^^^^^ feature has been removed
|
||||
# |
|
||||
# = note: removed in 1.58.0; see <https://github.com/rust-lang/rust/pull/138907; for more information
|
||||
# = note: merged into `doc_cfg`
|
||||
command = """
|
||||
rustup install nightly --profile minimal && cargo doc --no-deps --all-features
|
||||
rustup install nightly-2025-01-25 --profile minimal && cargo doc --no-deps --all-features
|
||||
"""
|
||||
publish = "target/doc"
|
||||
|
||||
@@ -8,8 +17,9 @@
|
||||
RUSTDOCFLAGS="""
|
||||
--cfg docsrs \
|
||||
--cfg tokio_unstable \
|
||||
--cfg tokio_taskdump \
|
||||
"""
|
||||
RUSTFLAGS="--cfg tokio_unstable --cfg docsrs"
|
||||
RUSTFLAGS="--cfg tokio_unstable --cfg tokio_taskdump --cfg docsrs"
|
||||
|
||||
[[redirects]]
|
||||
from = "/"
|
||||
|
||||
+307
@@ -0,0 +1,307 @@
|
||||
306
|
||||
&
|
||||
+
|
||||
<
|
||||
=
|
||||
>
|
||||
\
|
||||
~
|
||||
—
|
||||
0o777
|
||||
0s
|
||||
0xA
|
||||
0xD
|
||||
100ms
|
||||
100ns
|
||||
10ms
|
||||
10μs
|
||||
~12
|
||||
120s
|
||||
12.5%
|
||||
±1m
|
||||
±1ms
|
||||
1ms
|
||||
1s
|
||||
25%
|
||||
250ms
|
||||
2x
|
||||
~4
|
||||
443
|
||||
450ms
|
||||
50ms
|
||||
8MB
|
||||
ABI
|
||||
accessors
|
||||
adaptor
|
||||
adaptors
|
||||
Adaptors
|
||||
AIO
|
||||
ambient
|
||||
amongst
|
||||
api
|
||||
APIs
|
||||
async
|
||||
atomics
|
||||
awaitable
|
||||
backend
|
||||
backpressure
|
||||
backtrace
|
||||
backtraces
|
||||
backtracing
|
||||
binded
|
||||
bitfield
|
||||
bitfields
|
||||
Blockingly
|
||||
boolean
|
||||
broadcasted
|
||||
cancelled
|
||||
cancelling
|
||||
Cancelling
|
||||
CLI
|
||||
cloneable
|
||||
codebase
|
||||
codec
|
||||
codecs
|
||||
combinator
|
||||
combinators
|
||||
config
|
||||
Config
|
||||
connectionless
|
||||
coroutines
|
||||
cpu
|
||||
cpus
|
||||
cqe
|
||||
CQE
|
||||
cqe's
|
||||
customizable
|
||||
Customizable
|
||||
datagram
|
||||
Datagram
|
||||
datagrams
|
||||
deallocate
|
||||
deallocated
|
||||
Deallocates
|
||||
debuginfo
|
||||
decrement
|
||||
decrementing
|
||||
demangled
|
||||
dequeued
|
||||
dereferenced
|
||||
deregister
|
||||
deregistered
|
||||
deregistering
|
||||
deregisters
|
||||
Deregisters
|
||||
deregistration
|
||||
descriptor's
|
||||
destructor
|
||||
destructors
|
||||
destructure
|
||||
Destructures
|
||||
Dev
|
||||
dns
|
||||
DNS
|
||||
DoS
|
||||
dwOpenMode
|
||||
endian
|
||||
enqueue
|
||||
enqueued
|
||||
EntryInner
|
||||
enum
|
||||
eof
|
||||
Erlang's
|
||||
errored
|
||||
EWMA
|
||||
expirations
|
||||
fcntl
|
||||
fd
|
||||
fd's
|
||||
FIFOs
|
||||
filename
|
||||
filesystem
|
||||
filesystems
|
||||
fn
|
||||
fns
|
||||
FreeBSD
|
||||
frontend
|
||||
fs
|
||||
functionalities
|
||||
getters
|
||||
GID
|
||||
goroutines
|
||||
Growable
|
||||
gzip
|
||||
H2
|
||||
hashmaps
|
||||
HashMaps
|
||||
hashsets
|
||||
HdrHistogram
|
||||
ie
|
||||
Illumos
|
||||
impl
|
||||
implementers
|
||||
implementor
|
||||
implementors
|
||||
incrementing
|
||||
inlining
|
||||
interoperate
|
||||
invariants
|
||||
Invariants
|
||||
io
|
||||
IOCP
|
||||
iOS
|
||||
IOs
|
||||
io_uring
|
||||
IP
|
||||
IPv4
|
||||
IPv6
|
||||
iteratively
|
||||
Kotlin's
|
||||
latencies
|
||||
Lauck
|
||||
libc
|
||||
lifecycle
|
||||
lifo
|
||||
lookups
|
||||
macOS
|
||||
MacOS
|
||||
Marsaglia's
|
||||
metadata
|
||||
mio
|
||||
Mio
|
||||
mio's
|
||||
miri
|
||||
misconfigured
|
||||
mock's
|
||||
monomorphization
|
||||
mpmc
|
||||
mpsc
|
||||
multi
|
||||
Multi
|
||||
multicast
|
||||
Multithreaded
|
||||
mut
|
||||
mutex
|
||||
Mutex
|
||||
Nagle
|
||||
nonblocking
|
||||
nondecreasing
|
||||
noop
|
||||
ntasks
|
||||
ok
|
||||
oneshot
|
||||
ORed
|
||||
os
|
||||
overweighing
|
||||
parker
|
||||
parsers
|
||||
peekable
|
||||
PGID
|
||||
PID
|
||||
plaintext
|
||||
poller
|
||||
POSIX
|
||||
proxied
|
||||
qos
|
||||
RAII
|
||||
reallocations
|
||||
recv's
|
||||
refactors
|
||||
refcount
|
||||
refcounting
|
||||
repo
|
||||
repr
|
||||
representable
|
||||
reqwest
|
||||
reregistering
|
||||
resize
|
||||
resized
|
||||
RMW
|
||||
runtime
|
||||
runtime's
|
||||
runtimes
|
||||
rwlock
|
||||
rx
|
||||
scalability
|
||||
scheduler's
|
||||
semver
|
||||
setpgid
|
||||
sharded
|
||||
signalled
|
||||
signalling
|
||||
SmallCrush
|
||||
Solaris
|
||||
spawner
|
||||
Splitter
|
||||
spmc
|
||||
spsc
|
||||
src
|
||||
stabilised
|
||||
startup
|
||||
stateful
|
||||
stderr
|
||||
stdin
|
||||
stdout
|
||||
stealable
|
||||
stealer
|
||||
stealers
|
||||
struct
|
||||
structs
|
||||
subfield
|
||||
suboptimal
|
||||
subprocess
|
||||
superset
|
||||
symlink
|
||||
symlinks
|
||||
sys
|
||||
syscall
|
||||
syscalls
|
||||
tcp
|
||||
TCP
|
||||
TestU01
|
||||
threadpool
|
||||
timestamp
|
||||
timestamps
|
||||
TLS
|
||||
TOCTOU
|
||||
TODO
|
||||
tokio
|
||||
Tokio
|
||||
tokio's
|
||||
Tokio's
|
||||
tuple
|
||||
Tuple
|
||||
tx
|
||||
udp
|
||||
UDP
|
||||
UID
|
||||
unhandled
|
||||
unix
|
||||
unlink
|
||||
unpark
|
||||
Unpark
|
||||
unparked
|
||||
unparking
|
||||
unparks
|
||||
Unparks
|
||||
unreceived
|
||||
unsafety
|
||||
unsets
|
||||
Unsets
|
||||
unsynchronized
|
||||
untrusted
|
||||
uring
|
||||
usecases
|
||||
Valgrind
|
||||
Varghese
|
||||
vec
|
||||
versa
|
||||
versioned
|
||||
versioning
|
||||
vtable
|
||||
waker
|
||||
wakers
|
||||
Wakers
|
||||
wakeup
|
||||
wakeups
|
||||
workstealing
|
||||
@@ -0,0 +1,13 @@
|
||||
dev_comments = false
|
||||
skip_readme = false
|
||||
|
||||
[Hunspell]
|
||||
lang = "en_US"
|
||||
search_dirs = ["."]
|
||||
extra_dictionaries = ["spellcheck.dic"]
|
||||
skip_os_lookups = true
|
||||
use_builtin = true
|
||||
|
||||
[Hunspell.quirks]
|
||||
allow_concatenation = true
|
||||
|
||||
@@ -3,12 +3,16 @@ name = "stress-test"
|
||||
version = "0.1.0"
|
||||
authors = ["Tokio Contributors <[email protected]>"]
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
publish = false
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
tokio = { path = "../tokio/", features = ["full"] }
|
||||
tokio = { version = "1.0.0", path = "../tokio/", features = ["full"] }
|
||||
|
||||
[dev-dependencies]
|
||||
rand = "0.8"
|
||||
rand = "0.9"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
@@ -42,7 +42,7 @@ fn main() {
|
||||
.write_all(one_mega_random_bytes.as_slice())
|
||||
.await
|
||||
.unwrap();
|
||||
stream.read(&mut buff).await.unwrap();
|
||||
let _ = stream.read(&mut buff).await.unwrap();
|
||||
}
|
||||
tx.send(()).unwrap();
|
||||
});
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
This is used for the `no-atomic-u64-test` ci check that verifies that Tokio
|
||||
works even if the `AtomicU64` type is missing.
|
||||
|
||||
When increasing the nightly compiler version, you may need to regenerate this
|
||||
target using the following command:
|
||||
```
|
||||
rustc +nightly -Z unstable-options --print target-spec-json --target i686-unknown-linux-gnu | grep -v 'is-builtin' | sed 's/"max-atomic-width": 64/"max-atomic-width": 32/' > target-specs/i686-unknown-linux-gnu.json
|
||||
```
|
||||
|
||||
@@ -1,29 +1,35 @@
|
||||
{
|
||||
"arch": "x86",
|
||||
"cpu": "pentium4",
|
||||
"crt-objects-fallback": "false",
|
||||
"crt-static-respected": true,
|
||||
"data-layout": "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128",
|
||||
"data-layout": "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-i128:128-f64:32:64-f80:32-n8:16:32-S128",
|
||||
"dynamic-linking": true,
|
||||
"env": "gnu",
|
||||
"has-rpath": true,
|
||||
"has-thread-local": true,
|
||||
"linker-flavor": "gnu-cc",
|
||||
"llvm-target": "i686-unknown-linux-gnu",
|
||||
"max-atomic-width": 32,
|
||||
"metadata": {
|
||||
"description": null,
|
||||
"host_tools": null,
|
||||
"std": null,
|
||||
"tier": null
|
||||
},
|
||||
"os": "linux",
|
||||
"position-independent-executables": true,
|
||||
"pre-link-args": {
|
||||
"gcc": [
|
||||
"gnu-cc": [
|
||||
"-m32"
|
||||
],
|
||||
"gnu-lld-cc": [
|
||||
"-m32"
|
||||
]
|
||||
},
|
||||
"relro-level": "full",
|
||||
"stack-probes": {
|
||||
"kind": "inline-or-call",
|
||||
"min-llvm-version-for-inline": [
|
||||
16,
|
||||
0,
|
||||
0
|
||||
]
|
||||
"kind": "inline"
|
||||
},
|
||||
"supported-sanitizers": [
|
||||
"address"
|
||||
|
||||
@@ -3,6 +3,7 @@ name = "tests-build"
|
||||
version = "0.1.0"
|
||||
authors = ["Tokio Contributors <[email protected]>"]
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
publish = false
|
||||
|
||||
[features]
|
||||
@@ -10,7 +11,10 @@ full = ["tokio/full"]
|
||||
rt = ["tokio/rt", "tokio/macros"]
|
||||
|
||||
[dependencies]
|
||||
tokio = { path = "../tokio", optional = true }
|
||||
tokio = { version = "1.0.0", path = "../tokio", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
trybuild = "1.0"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
@@ -6,5 +6,5 @@ To run all of the tests in this directory, run the following commands:
|
||||
cargo test --features full
|
||||
cargo test --features rt
|
||||
```
|
||||
If one of the tests fail, you can pass `TRYBUILD=overwrite` to the `cargo test`
|
||||
If any of the tests fail, you can pass `TRYBUILD=overwrite` to the `cargo test`
|
||||
command that failed to have it regenerate the test output.
|
||||
|
||||
@@ -41,8 +41,31 @@ async fn test_crate_not_path_int() {}
|
||||
#[tokio::test(crate = "456")]
|
||||
async fn test_crate_not_path_invalid() {}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", unhandled_panic = "shutdown_runtime")]
|
||||
async fn test_multi_thread_with_unhandled_panic() {}
|
||||
|
||||
#[tokio::test]
|
||||
#[test]
|
||||
async fn test_has_second_test_attr() {}
|
||||
|
||||
#[tokio::test]
|
||||
#[::core::prelude::v1::test]
|
||||
async fn test_has_second_test_attr_v1() {}
|
||||
|
||||
#[tokio::test]
|
||||
#[core::prelude::rust_2015::test]
|
||||
async fn test_has_second_test_attr_rust_2015() {}
|
||||
|
||||
#[tokio::test]
|
||||
#[::std::prelude::rust_2018::test]
|
||||
async fn test_has_second_test_attr_rust_2018() {}
|
||||
|
||||
#[tokio::test]
|
||||
#[std::prelude::rust_2021::test]
|
||||
async fn test_has_second_test_attr_rust_2021() {}
|
||||
|
||||
#[tokio::test]
|
||||
#[tokio::test]
|
||||
async fn test_has_generated_second_test_attr() {}
|
||||
|
||||
fn main() {}
|
||||
|
||||
@@ -1,95 +1,121 @@
|
||||
error: the `async` keyword is missing from the function declaration
|
||||
--> $DIR/macros_invalid_input.rs:6:1
|
||||
--> tests/fail/macros_invalid_input.rs:6:1
|
||||
|
|
||||
6 | fn main_is_not_async() {}
|
||||
| ^^
|
||||
|
||||
error: Unknown attribute foo is specified; expected one of: `flavor`, `worker_threads`, `start_paused`, `crate`
|
||||
--> $DIR/macros_invalid_input.rs:8:15
|
||||
error: Unknown attribute foo is specified; expected one of: `flavor`, `worker_threads`, `start_paused`, `crate`, `unhandled_panic`.
|
||||
--> tests/fail/macros_invalid_input.rs:8:15
|
||||
|
|
||||
8 | #[tokio::main(foo)]
|
||||
| ^^^
|
||||
|
||||
error: Must have specified ident
|
||||
--> $DIR/macros_invalid_input.rs:11:15
|
||||
--> tests/fail/macros_invalid_input.rs:11:15
|
||||
|
|
||||
11 | #[tokio::main(threadpool::bar)]
|
||||
| ^^^^^^^^^^^^^^^
|
||||
|
||||
error: the `async` keyword is missing from the function declaration
|
||||
--> $DIR/macros_invalid_input.rs:15:1
|
||||
--> tests/fail/macros_invalid_input.rs:15:1
|
||||
|
|
||||
15 | fn test_is_not_async() {}
|
||||
| ^^
|
||||
|
||||
error: Unknown attribute foo is specified; expected one of: `flavor`, `worker_threads`, `start_paused`, `crate`
|
||||
--> $DIR/macros_invalid_input.rs:17:15
|
||||
error: Unknown attribute foo is specified; expected one of: `flavor`, `worker_threads`, `start_paused`, `crate`, `unhandled_panic`.
|
||||
--> tests/fail/macros_invalid_input.rs:17:15
|
||||
|
|
||||
17 | #[tokio::test(foo)]
|
||||
| ^^^
|
||||
|
||||
error: Unknown attribute foo is specified; expected one of: `flavor`, `worker_threads`, `start_paused`, `crate`
|
||||
--> $DIR/macros_invalid_input.rs:20:15
|
||||
error: Unknown attribute foo is specified; expected one of: `flavor`, `worker_threads`, `start_paused`, `crate`, `unhandled_panic`
|
||||
--> tests/fail/macros_invalid_input.rs:20:15
|
||||
|
|
||||
20 | #[tokio::test(foo = 123)]
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: Failed to parse value of `flavor` as string.
|
||||
--> $DIR/macros_invalid_input.rs:23:24
|
||||
--> tests/fail/macros_invalid_input.rs:23:24
|
||||
|
|
||||
23 | #[tokio::test(flavor = 123)]
|
||||
| ^^^
|
||||
|
||||
error: No such runtime flavor `foo`. The runtime flavors are `current_thread` and `multi_thread`.
|
||||
--> $DIR/macros_invalid_input.rs:26:24
|
||||
--> tests/fail/macros_invalid_input.rs:26:24
|
||||
|
|
||||
26 | #[tokio::test(flavor = "foo")]
|
||||
| ^^^^^
|
||||
|
||||
error: The `start_paused` option requires the `current_thread` runtime flavor. Use `#[tokio::test(flavor = "current_thread")]`
|
||||
--> $DIR/macros_invalid_input.rs:29:55
|
||||
--> tests/fail/macros_invalid_input.rs:29:55
|
||||
|
|
||||
29 | #[tokio::test(flavor = "multi_thread", start_paused = false)]
|
||||
| ^^^^^
|
||||
|
||||
error: Failed to parse value of `worker_threads` as integer.
|
||||
--> $DIR/macros_invalid_input.rs:32:57
|
||||
--> tests/fail/macros_invalid_input.rs:32:57
|
||||
|
|
||||
32 | #[tokio::test(flavor = "multi_thread", worker_threads = "foo")]
|
||||
| ^^^^^
|
||||
|
||||
error: The `worker_threads` option requires the `multi_thread` runtime flavor. Use `#[tokio::test(flavor = "multi_thread")]`
|
||||
--> $DIR/macros_invalid_input.rs:35:59
|
||||
--> tests/fail/macros_invalid_input.rs:35:59
|
||||
|
|
||||
35 | #[tokio::test(flavor = "current_thread", worker_threads = 4)]
|
||||
| ^
|
||||
|
||||
error: Failed to parse value of `crate` as path.
|
||||
--> $DIR/macros_invalid_input.rs:38:23
|
||||
--> tests/fail/macros_invalid_input.rs:38:23
|
||||
|
|
||||
38 | #[tokio::test(crate = 456)]
|
||||
| ^^^
|
||||
|
||||
error: Failed to parse value of `crate` as path: "456"
|
||||
--> $DIR/macros_invalid_input.rs:41:23
|
||||
--> tests/fail/macros_invalid_input.rs:41:23
|
||||
|
|
||||
41 | #[tokio::test(crate = "456")]
|
||||
| ^^^^^
|
||||
|
||||
error: second test attribute is supplied
|
||||
--> $DIR/macros_invalid_input.rs:45:1
|
||||
error: The `unhandled_panic` option requires the `current_thread` runtime flavor. Use `#[tokio::test(flavor = "current_thread")]`
|
||||
--> tests/fail/macros_invalid_input.rs:44:58
|
||||
|
|
||||
45 | #[test]
|
||||
44 | #[tokio::test(flavor = "multi_thread", unhandled_panic = "shutdown_runtime")]
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: second test attribute is supplied, consider removing or changing the order of your test attributes
|
||||
--> tests/fail/macros_invalid_input.rs:48:1
|
||||
|
|
||||
48 | #[test]
|
||||
| ^^^^^^^
|
||||
|
||||
error: duplicated attribute
|
||||
--> $DIR/macros_invalid_input.rs:45:1
|
||||
error: second test attribute is supplied, consider removing or changing the order of your test attributes
|
||||
--> tests/fail/macros_invalid_input.rs:52:1
|
||||
|
|
||||
45 | #[test]
|
||||
| ^^^^^^^
|
||||
52 | #[::core::prelude::v1::test]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: second test attribute is supplied, consider removing or changing the order of your test attributes
|
||||
--> tests/fail/macros_invalid_input.rs:56:1
|
||||
|
|
||||
note: the lint level is defined here
|
||||
--> $DIR/macros_invalid_input.rs:1:9
|
||||
56 | #[core::prelude::rust_2015::test]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: second test attribute is supplied, consider removing or changing the order of your test attributes
|
||||
--> tests/fail/macros_invalid_input.rs:60:1
|
||||
|
|
||||
1 | #![deny(duplicate_macro_attributes)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
60 | #[::std::prelude::rust_2018::test]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: second test attribute is supplied, consider removing or changing the order of your test attributes
|
||||
--> tests/fail/macros_invalid_input.rs:64:1
|
||||
|
|
||||
64 | #[std::prelude::rust_2021::test]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: second test attribute is supplied, consider removing or changing the order of your test attributes
|
||||
--> tests/fail/macros_invalid_input.rs:67:1
|
||||
|
|
||||
67 | #[tokio::test]
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: this error originates in the attribute macro `tokio::test` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
@@ -23,6 +23,40 @@ async fn extra_semicolon() -> Result<(), ()> {
|
||||
Ok(());
|
||||
}
|
||||
|
||||
/// This test is a characterization test for the `?` operator.
|
||||
///
|
||||
/// See <https://github.com/tokio-rs/tokio/issues/6930#issuecomment-2572502517> for more details.
|
||||
///
|
||||
/// It should fail with a single error message about the return type of the function, but instead
|
||||
/// if fails with an extra error message due to the `?` operator being used within the async block
|
||||
/// rather than the original function.
|
||||
///
|
||||
/// ```text
|
||||
/// 28 | None?;
|
||||
/// | ^ cannot use the `?` operator in an async block that returns `()`
|
||||
/// ```
|
||||
#[tokio::main]
|
||||
async fn question_mark_operator_with_invalid_option() -> Option<()> {
|
||||
None?;
|
||||
}
|
||||
|
||||
/// This test is a characterization test for the `?` operator.
|
||||
///
|
||||
/// See <https://github.com/tokio-rs/tokio/issues/6930#issuecomment-2572502517> for more details.
|
||||
///
|
||||
/// It should fail with a single error message about the return type of the function, but instead
|
||||
/// if fails with an extra error message due to the `?` operator being used within the async block
|
||||
/// rather than the original function.
|
||||
///
|
||||
/// ```text
|
||||
/// 33 | Ok(())?;
|
||||
/// | ^ cannot use the `?` operator in an async block that returns `()`
|
||||
/// ```
|
||||
#[tokio::main]
|
||||
async fn question_mark_operator_with_invalid_result() -> Result<(), ()> {
|
||||
Ok(())?;
|
||||
}
|
||||
|
||||
// https://github.com/tokio-rs/tokio/issues/4635
|
||||
#[allow(redundant_semicolons)]
|
||||
#[rustfmt::skip]
|
||||
|
||||
@@ -1,24 +1,36 @@
|
||||
error[E0308]: mismatched types
|
||||
--> tests/fail/macros_type_mismatch.rs:5:5
|
||||
|
|
||||
4 | async fn missing_semicolon_or_return_type() {
|
||||
| - help: a return type might be missing here: `-> _`
|
||||
5 | Ok(())
|
||||
| ^^^^^^ expected `()`, found `Result<(), _>`
|
||||
|
|
||||
= note: expected unit type `()`
|
||||
found enum `Result<(), _>`
|
||||
help: a return type might be missing here
|
||||
|
|
||||
4 | async fn missing_semicolon_or_return_type() -> _ {
|
||||
| ++++
|
||||
help: consider using `Result::expect` to unwrap the `Result<(), _>` value, panicking if the value is a `Result::Err`
|
||||
|
|
||||
5 | Ok(()).expect("REASON")
|
||||
| +++++++++++++++++
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> tests/fail/macros_type_mismatch.rs:10:5
|
||||
|
|
||||
9 | async fn missing_return_type() {
|
||||
| - help: a return type might be missing here: `-> _`
|
||||
10 | return Ok(());
|
||||
| ^^^^^^^^^^^^^^ expected `()`, found `Result<(), _>`
|
||||
|
|
||||
= note: expected unit type `()`
|
||||
found enum `Result<(), _>`
|
||||
help: a return type might be missing here
|
||||
|
|
||||
9 | async fn missing_return_type() -> _ {
|
||||
| ++++
|
||||
help: consider using `Result::expect` to unwrap the `Result<(), _>` value, panicking if the value is a `Result::Err`
|
||||
|
|
||||
10 | return Ok(());.expect("REASON")
|
||||
| +++++++++++++++++
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> tests/fail/macros_type_mismatch.rs:23:5
|
||||
@@ -37,11 +49,64 @@ help: try adding an expression at the end of the block
|
||||
24 + Ok(())
|
||||
|
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> tests/fail/macros_type_mismatch.rs:32:5
|
||||
error[E0277]: the `?` operator can only be used in an async block that returns `Result` or `Option` (or another type that implements `FromResidual`)
|
||||
--> tests/fail/macros_type_mismatch.rs:40:9
|
||||
|
|
||||
30 | async fn issue_4635() {
|
||||
| - help: try adding a return type: `-> i32`
|
||||
31 | return 1;
|
||||
32 | ;
|
||||
38 | #[tokio::main]
|
||||
| -------------- this function should return `Result` or `Option` to accept `?`
|
||||
39 | async fn question_mark_operator_with_invalid_option() -> Option<()> {
|
||||
40 | None?;
|
||||
| ^ cannot use the `?` operator in an async block that returns `()`
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> tests/fail/macros_type_mismatch.rs:40:5
|
||||
|
|
||||
39 | async fn question_mark_operator_with_invalid_option() -> Option<()> {
|
||||
| ---------- expected `Option<()>` because of return type
|
||||
40 | None?;
|
||||
| ^^^^^^ expected `Option<()>`, found `()`
|
||||
|
|
||||
= note: expected enum `Option<()>`
|
||||
found unit type `()`
|
||||
help: try adding an expression at the end of the block
|
||||
|
|
||||
40 ~ None?;;
|
||||
41 + None
|
||||
|
|
||||
40 ~ None?;;
|
||||
41 + Some(())
|
||||
|
|
||||
|
||||
error[E0277]: the `?` operator can only be used in an async block that returns `Result` or `Option` (or another type that implements `FromResidual`)
|
||||
--> tests/fail/macros_type_mismatch.rs:57:11
|
||||
|
|
||||
55 | #[tokio::main]
|
||||
| -------------- this function should return `Result` or `Option` to accept `?`
|
||||
56 | async fn question_mark_operator_with_invalid_result() -> Result<(), ()> {
|
||||
57 | Ok(())?;
|
||||
| ^ cannot use the `?` operator in an async block that returns `()`
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> tests/fail/macros_type_mismatch.rs:57:5
|
||||
|
|
||||
56 | async fn question_mark_operator_with_invalid_result() -> Result<(), ()> {
|
||||
| -------------- expected `Result<(), ()>` because of return type
|
||||
57 | Ok(())?;
|
||||
| ^^^^^^^^ expected `Result<(), ()>`, found `()`
|
||||
|
|
||||
= note: expected enum `Result<(), ()>`
|
||||
found unit type `()`
|
||||
help: try adding an expression at the end of the block
|
||||
|
|
||||
57 ~ Ok(())?;;
|
||||
58 + Ok(())
|
||||
|
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> tests/fail/macros_type_mismatch.rs:66:5
|
||||
|
|
||||
64 | async fn issue_4635() {
|
||||
| - help: try adding a return type: `-> i32`
|
||||
65 | return 1;
|
||||
66 | ;
|
||||
| ^ expected `()`, found integer
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#[test]
|
||||
#[cfg_attr(miri, ignore)]
|
||||
fn compile_fail_full() {
|
||||
let t = trybuild::TestCases::new();
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ name = "tests-integration"
|
||||
version = "0.1.0"
|
||||
authors = ["Tokio Contributors <[email protected]>"]
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
publish = false
|
||||
|
||||
[[bin]]
|
||||
@@ -39,6 +40,7 @@ rt-process-signal = ["rt-net", "tokio/process", "tokio/signal"]
|
||||
# This is an explicit feature so we can use `cargo hack` testing single features
|
||||
# instead of all possible permutations.
|
||||
wasi-rt = ["rt", "macros", "sync"]
|
||||
wasi-threads-rt = ["wasi-rt", "rt-multi-thread"]
|
||||
|
||||
full = [
|
||||
"macros",
|
||||
@@ -54,8 +56,11 @@ rt = ["tokio/rt"]
|
||||
rt-multi-thread = ["rt", "tokio/rt-multi-thread"]
|
||||
|
||||
[dependencies]
|
||||
tokio = { path = "../tokio" }
|
||||
tokio-test = { path = "../tokio-test", optional = true }
|
||||
tokio = { version = "1.0.0", path = "../tokio" }
|
||||
tokio-test = { version = "0.4", path = "../tokio-test", optional = true }
|
||||
doc-comment = "0.3.1"
|
||||
futures = { version = "0.3.0", features = ["async-await"] }
|
||||
bytes = "1.0.0"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use futures::future::poll_fn;
|
||||
use std::future::poll_fn;
|
||||
|
||||
fn main() {
|
||||
let rt = tokio::runtime::Builder::new_multi_thread()
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
#![cfg(all(
|
||||
feature = "macros",
|
||||
feature = "rt-multi-thread",
|
||||
not(target_os = "wasi")
|
||||
))]
|
||||
#![cfg(all(feature = "macros", feature = "rt-multi-thread"))]
|
||||
|
||||
#[tokio::main]
|
||||
async fn basic_main() -> usize {
|
||||
|
||||
@@ -4,7 +4,10 @@ use futures::channel::oneshot;
|
||||
use futures::executor::block_on;
|
||||
use std::thread;
|
||||
|
||||
#[cfg_attr(target_os = "wasi", ignore = "WASI: std::thread::spawn not supported")]
|
||||
#[cfg_attr(
|
||||
not(feature = "rt-multi-thread"),
|
||||
ignore = "WASI: std::thread::spawn not supported"
|
||||
)]
|
||||
#[test]
|
||||
fn join_with_select() {
|
||||
block_on(async {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#![warn(rust_2018_idioms)]
|
||||
#![cfg(all(feature = "full", not(target_os = "wasi")))]
|
||||
#![cfg(all(feature = "full", not(target_os = "wasi"), not(miri)))]
|
||||
|
||||
use tokio::io::{AsyncBufReadExt, AsyncReadExt, AsyncWriteExt, BufReader};
|
||||
use tokio::join;
|
||||
@@ -10,6 +10,7 @@ use futures::future::{self, FutureExt};
|
||||
use std::env;
|
||||
use std::io;
|
||||
use std::process::{ExitStatus, Stdio};
|
||||
use std::task::ready;
|
||||
|
||||
fn cat() -> Command {
|
||||
let mut cmd = Command::new(env!("CARGO_BIN_EXE_test-cat"));
|
||||
@@ -24,7 +25,7 @@ async fn feed_cat(mut cat: Child, n: usize) -> io::Result<ExitStatus> {
|
||||
// Produce n lines on the child's stdout.
|
||||
let write = async {
|
||||
for i in 0..n {
|
||||
let bytes = format!("line {}\n", i).into_bytes();
|
||||
let bytes = format!("line {i}\n").into_bytes();
|
||||
stdin.write_all(&bytes).await.unwrap();
|
||||
}
|
||||
|
||||
@@ -51,7 +52,7 @@ async fn feed_cat(mut cat: Child, n: usize) -> io::Result<ExitStatus> {
|
||||
(false, 0) => panic!("broken pipe"),
|
||||
(true, n) if n != 0 => panic!("extraneous data"),
|
||||
_ => {
|
||||
let expected = format!("line {}", num_lines);
|
||||
let expected = format!("line {num_lines}");
|
||||
assert_eq!(expected, data);
|
||||
}
|
||||
};
|
||||
@@ -205,13 +206,13 @@ async fn vectored_writes() {
|
||||
let mut input = Bytes::from_static(b"hello\n").chain(Bytes::from_static(b"world!\n"));
|
||||
let mut writes_completed = 0;
|
||||
|
||||
futures::future::poll_fn(|cx| loop {
|
||||
std::future::poll_fn(|cx| loop {
|
||||
let mut slices = [IoSlice::new(&[]); 2];
|
||||
let vectored = input.chunks_vectored(&mut slices);
|
||||
if vectored == 0 {
|
||||
return std::task::Poll::Ready(std::io::Result::Ok(()));
|
||||
}
|
||||
let n = futures::ready!(Pin::new(&mut stdin).poll_write_vectored(cx, &slices))?;
|
||||
let n = ready!(Pin::new(&mut stdin).poll_write_vectored(cx, &slices))?;
|
||||
writes_completed += 1;
|
||||
input.advance(n);
|
||||
})
|
||||
|
||||
@@ -1,3 +1,31 @@
|
||||
# 2.5.0 (Jan 8th, 2025)
|
||||
|
||||
- macros: suppress `clippy::needless_return` in `#[tokio::main]` ([#6874])
|
||||
|
||||
[#6874]: https://github.com/tokio-rs/tokio/pull/6874
|
||||
|
||||
# 2.4.0 (July 22nd, 2024)
|
||||
|
||||
- msrv: increase MSRV to 1.70 ([#6645])
|
||||
- macros: allow `unhandled_panic` behavior for `#[tokio::main]` and `#[tokio::test]` ([#6593])
|
||||
|
||||
[#6593]: https://github.com/tokio-rs/tokio/pull/6593
|
||||
[#6645]: https://github.com/tokio-rs/tokio/pull/6645
|
||||
|
||||
# 2.3.0 (May 30th, 2024)
|
||||
|
||||
- macros: make `#[tokio::test]` append `#[test]` at the end of the attribute list ([#6497])
|
||||
|
||||
[#6497]: https://github.com/tokio-rs/tokio/pull/6497
|
||||
|
||||
# 2.2.0 (November 19th, 2023)
|
||||
|
||||
### Changed
|
||||
|
||||
- use `::core` qualified imports instead of `::std` inside `tokio::test` macro ([#5973])
|
||||
|
||||
[#5973]: https://github.com/tokio-rs/tokio/pull/5973
|
||||
|
||||
# 2.1.0 (April 25th, 2023)
|
||||
|
||||
- macros: fix typo in `#[tokio::test]` docs ([#5636])
|
||||
|
||||
@@ -4,9 +4,9 @@ name = "tokio-macros"
|
||||
# - Remove path dependencies
|
||||
# - Update CHANGELOG.md.
|
||||
# - Create "tokio-macros-1.x.y" git tag.
|
||||
version = "2.1.0"
|
||||
version = "2.5.0"
|
||||
edition = "2021"
|
||||
rust-version = "1.56"
|
||||
rust-version = "1.70"
|
||||
authors = ["Tokio Contributors <[email protected]>"]
|
||||
license = "MIT"
|
||||
repository = "https://github.com/tokio-rs/tokio"
|
||||
@@ -22,7 +22,7 @@ proc-macro = true
|
||||
[features]
|
||||
|
||||
[dependencies]
|
||||
proc-macro2 = "1.0.7"
|
||||
proc-macro2 = "1.0.60"
|
||||
quote = "1"
|
||||
syn = { version = "2.0", features = ["full"] }
|
||||
|
||||
@@ -31,3 +31,6 @@ tokio = { version = "1.0.0", path = "../tokio", features = ["full"] }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
+2
-27
@@ -1,32 +1,7 @@
|
||||
Copyright (c) 2023 Tokio Contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any
|
||||
person obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the
|
||||
Software without restriction, including without
|
||||
limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software
|
||||
is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice
|
||||
shall be included in all copies or substantial portions
|
||||
of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
The MIT License (MIT)
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 Yoshua Wuyts
|
||||
Copyright (c) Tokio Contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
+131
-35
@@ -20,7 +20,32 @@ impl RuntimeFlavor {
|
||||
"single_thread" => Err("The single threaded runtime flavor is called `current_thread`.".to_string()),
|
||||
"basic_scheduler" => Err("The `basic_scheduler` runtime flavor has been renamed to `current_thread`.".to_string()),
|
||||
"threaded_scheduler" => Err("The `threaded_scheduler` runtime flavor has been renamed to `multi_thread`.".to_string()),
|
||||
_ => Err(format!("No such runtime flavor `{}`. The runtime flavors are `current_thread` and `multi_thread`.", s)),
|
||||
_ => Err(format!("No such runtime flavor `{s}`. The runtime flavors are `current_thread` and `multi_thread`.")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
enum UnhandledPanic {
|
||||
Ignore,
|
||||
ShutdownRuntime,
|
||||
}
|
||||
|
||||
impl UnhandledPanic {
|
||||
fn from_str(s: &str) -> Result<UnhandledPanic, String> {
|
||||
match s {
|
||||
"ignore" => Ok(UnhandledPanic::Ignore),
|
||||
"shutdown_runtime" => Ok(UnhandledPanic::ShutdownRuntime),
|
||||
_ => Err(format!("No such unhandled panic behavior `{s}`. The unhandled panic behaviors are `ignore` and `shutdown_runtime`.")),
|
||||
}
|
||||
}
|
||||
|
||||
fn into_tokens(self, crate_path: &TokenStream) -> TokenStream {
|
||||
match self {
|
||||
UnhandledPanic::Ignore => quote! { #crate_path::runtime::UnhandledPanic::Ignore },
|
||||
UnhandledPanic::ShutdownRuntime => {
|
||||
quote! { #crate_path::runtime::UnhandledPanic::ShutdownRuntime }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -30,6 +55,7 @@ struct FinalConfig {
|
||||
worker_threads: Option<usize>,
|
||||
start_paused: Option<bool>,
|
||||
crate_name: Option<Path>,
|
||||
unhandled_panic: Option<UnhandledPanic>,
|
||||
}
|
||||
|
||||
/// Config used in case of the attribute not being able to build a valid config
|
||||
@@ -38,6 +64,7 @@ const DEFAULT_ERROR_CONFIG: FinalConfig = FinalConfig {
|
||||
worker_threads: None,
|
||||
start_paused: None,
|
||||
crate_name: None,
|
||||
unhandled_panic: None,
|
||||
};
|
||||
|
||||
struct Configuration {
|
||||
@@ -48,6 +75,7 @@ struct Configuration {
|
||||
start_paused: Option<(bool, Span)>,
|
||||
is_test: bool,
|
||||
crate_name: Option<Path>,
|
||||
unhandled_panic: Option<(UnhandledPanic, Span)>,
|
||||
}
|
||||
|
||||
impl Configuration {
|
||||
@@ -63,6 +91,7 @@ impl Configuration {
|
||||
start_paused: None,
|
||||
is_test,
|
||||
crate_name: None,
|
||||
unhandled_panic: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,6 +146,25 @@ impl Configuration {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_unhandled_panic(
|
||||
&mut self,
|
||||
unhandled_panic: syn::Lit,
|
||||
span: Span,
|
||||
) -> Result<(), syn::Error> {
|
||||
if self.unhandled_panic.is_some() {
|
||||
return Err(syn::Error::new(
|
||||
span,
|
||||
"`unhandled_panic` set multiple times.",
|
||||
));
|
||||
}
|
||||
|
||||
let unhandled_panic = parse_string(unhandled_panic, span, "unhandled_panic")?;
|
||||
let unhandled_panic =
|
||||
UnhandledPanic::from_str(&unhandled_panic).map_err(|err| syn::Error::new(span, err))?;
|
||||
self.unhandled_panic = Some((unhandled_panic, span));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn macro_name(&self) -> &'static str {
|
||||
if self.is_test {
|
||||
"tokio::test"
|
||||
@@ -126,22 +174,22 @@ impl Configuration {
|
||||
}
|
||||
|
||||
fn build(&self) -> Result<FinalConfig, syn::Error> {
|
||||
let flavor = self.flavor.unwrap_or(self.default_flavor);
|
||||
use RuntimeFlavor::*;
|
||||
use RuntimeFlavor as F;
|
||||
|
||||
let flavor = self.flavor.unwrap_or(self.default_flavor);
|
||||
let worker_threads = match (flavor, self.worker_threads) {
|
||||
(CurrentThread, Some((_, worker_threads_span))) => {
|
||||
(F::CurrentThread, Some((_, worker_threads_span))) => {
|
||||
let msg = format!(
|
||||
"The `worker_threads` option requires the `multi_thread` runtime flavor. Use `#[{}(flavor = \"multi_thread\")]`",
|
||||
self.macro_name(),
|
||||
);
|
||||
return Err(syn::Error::new(worker_threads_span, msg));
|
||||
}
|
||||
(CurrentThread, None) => None,
|
||||
(Threaded, worker_threads) if self.rt_multi_thread_available => {
|
||||
(F::CurrentThread, None) => None,
|
||||
(F::Threaded, worker_threads) if self.rt_multi_thread_available => {
|
||||
worker_threads.map(|(val, _span)| val)
|
||||
}
|
||||
(Threaded, _) => {
|
||||
(F::Threaded, _) => {
|
||||
let msg = if self.flavor.is_none() {
|
||||
"The default runtime flavor is `multi_thread`, but the `rt-multi-thread` feature is disabled."
|
||||
} else {
|
||||
@@ -152,14 +200,26 @@ impl Configuration {
|
||||
};
|
||||
|
||||
let start_paused = match (flavor, self.start_paused) {
|
||||
(Threaded, Some((_, start_paused_span))) => {
|
||||
(F::Threaded, Some((_, start_paused_span))) => {
|
||||
let msg = format!(
|
||||
"The `start_paused` option requires the `current_thread` runtime flavor. Use `#[{}(flavor = \"current_thread\")]`",
|
||||
self.macro_name(),
|
||||
);
|
||||
return Err(syn::Error::new(start_paused_span, msg));
|
||||
}
|
||||
(CurrentThread, Some((start_paused, _))) => Some(start_paused),
|
||||
(F::CurrentThread, Some((start_paused, _))) => Some(start_paused),
|
||||
(_, None) => None,
|
||||
};
|
||||
|
||||
let unhandled_panic = match (flavor, self.unhandled_panic) {
|
||||
(F::Threaded, Some((_, unhandled_panic_span))) => {
|
||||
let msg = format!(
|
||||
"The `unhandled_panic` option requires the `current_thread` runtime flavor. Use `#[{}(flavor = \"current_thread\")]`",
|
||||
self.macro_name(),
|
||||
);
|
||||
return Err(syn::Error::new(unhandled_panic_span, msg));
|
||||
}
|
||||
(F::CurrentThread, Some((unhandled_panic, _))) => Some(unhandled_panic),
|
||||
(_, None) => None,
|
||||
};
|
||||
|
||||
@@ -168,6 +228,7 @@ impl Configuration {
|
||||
flavor,
|
||||
worker_threads,
|
||||
start_paused,
|
||||
unhandled_panic,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -178,12 +239,12 @@ fn parse_int(int: syn::Lit, span: Span, field: &str) -> Result<usize, syn::Error
|
||||
Ok(value) => Ok(value),
|
||||
Err(e) => Err(syn::Error::new(
|
||||
span,
|
||||
format!("Failed to parse value of `{}` as integer: {}", field, e),
|
||||
format!("Failed to parse value of `{field}` as integer: {e}"),
|
||||
)),
|
||||
},
|
||||
_ => Err(syn::Error::new(
|
||||
span,
|
||||
format!("Failed to parse value of `{}` as integer.", field),
|
||||
format!("Failed to parse value of `{field}` as integer."),
|
||||
)),
|
||||
}
|
||||
}
|
||||
@@ -194,7 +255,7 @@ fn parse_string(int: syn::Lit, span: Span, field: &str) -> Result<String, syn::E
|
||||
syn::Lit::Verbatim(s) => Ok(s.to_string()),
|
||||
_ => Err(syn::Error::new(
|
||||
span,
|
||||
format!("Failed to parse value of `{}` as string.", field),
|
||||
format!("Failed to parse value of `{field}` as string."),
|
||||
)),
|
||||
}
|
||||
}
|
||||
@@ -214,7 +275,7 @@ fn parse_path(lit: syn::Lit, span: Span, field: &str) -> Result<Path, syn::Error
|
||||
}
|
||||
_ => Err(syn::Error::new(
|
||||
span,
|
||||
format!("Failed to parse value of `{}` as path.", field),
|
||||
format!("Failed to parse value of `{field}` as path."),
|
||||
)),
|
||||
}
|
||||
}
|
||||
@@ -224,7 +285,7 @@ fn parse_bool(bool: syn::Lit, span: Span, field: &str) -> Result<bool, syn::Erro
|
||||
syn::Lit::Bool(b) => Ok(b.value),
|
||||
_ => Err(syn::Error::new(
|
||||
span,
|
||||
format!("Failed to parse value of `{}` as bool.", field),
|
||||
format!("Failed to parse value of `{field}` as bool."),
|
||||
)),
|
||||
}
|
||||
}
|
||||
@@ -275,10 +336,13 @@ fn build_config(
|
||||
"crate" => {
|
||||
config.set_crate_name(lit.clone(), syn::spanned::Spanned::span(lit))?;
|
||||
}
|
||||
"unhandled_panic" => {
|
||||
config
|
||||
.set_unhandled_panic(lit.clone(), syn::spanned::Spanned::span(lit))?;
|
||||
}
|
||||
name => {
|
||||
let msg = format!(
|
||||
"Unknown attribute {} is specified; expected one of: `flavor`, `worker_threads`, `start_paused`, `crate`",
|
||||
name,
|
||||
"Unknown attribute {name} is specified; expected one of: `flavor`, `worker_threads`, `start_paused`, `crate`, `unhandled_panic`",
|
||||
);
|
||||
return Err(syn::Error::new_spanned(namevalue, msg));
|
||||
}
|
||||
@@ -293,21 +357,19 @@ fn build_config(
|
||||
let msg = match name.as_str() {
|
||||
"threaded_scheduler" | "multi_thread" => {
|
||||
format!(
|
||||
"Set the runtime flavor with #[{}(flavor = \"multi_thread\")].",
|
||||
macro_name
|
||||
"Set the runtime flavor with #[{macro_name}(flavor = \"multi_thread\")]."
|
||||
)
|
||||
}
|
||||
"basic_scheduler" | "current_thread" | "single_threaded" => {
|
||||
format!(
|
||||
"Set the runtime flavor with #[{}(flavor = \"current_thread\")].",
|
||||
macro_name
|
||||
"Set the runtime flavor with #[{macro_name}(flavor = \"current_thread\")]."
|
||||
)
|
||||
}
|
||||
"flavor" | "worker_threads" | "start_paused" => {
|
||||
format!("The `{}` attribute requires an argument.", name)
|
||||
"flavor" | "worker_threads" | "start_paused" | "crate" | "unhandled_panic" => {
|
||||
format!("The `{name}` attribute requires an argument.")
|
||||
}
|
||||
name => {
|
||||
format!("Unknown attribute {} is specified; expected one of: `flavor`, `worker_threads`, `start_paused`, `crate`", name)
|
||||
format!("Unknown attribute {name} is specified; expected one of: `flavor`, `worker_threads`, `start_paused`, `crate`, `unhandled_panic`.")
|
||||
}
|
||||
};
|
||||
return Err(syn::Error::new_spanned(path, msg));
|
||||
@@ -359,8 +421,12 @@ fn parse_knobs(mut input: ItemFn, is_test: bool, config: FinalConfig) -> TokenSt
|
||||
if let Some(v) = config.start_paused {
|
||||
rt = quote_spanned! {last_stmt_start_span=> #rt.start_paused(#v) };
|
||||
}
|
||||
if let Some(v) = config.unhandled_panic {
|
||||
let unhandled_panic = v.into_tokens(&crate_path);
|
||||
rt = quote_spanned! {last_stmt_start_span=> #rt.unhandled_panic(#unhandled_panic) };
|
||||
}
|
||||
|
||||
let header = if is_test {
|
||||
let generated_attrs = if is_test {
|
||||
quote! {
|
||||
#[::core::prelude::v1::test]
|
||||
}
|
||||
@@ -369,8 +435,9 @@ fn parse_knobs(mut input: ItemFn, is_test: bool, config: FinalConfig) -> TokenSt
|
||||
};
|
||||
|
||||
let body_ident = quote! { body };
|
||||
// This explicit `return` is intentional. See tokio-rs/tokio#4636
|
||||
let last_block = quote_spanned! {last_stmt_end_span=>
|
||||
#[allow(clippy::expect_used, clippy::diverging_sub_expression)]
|
||||
#[allow(clippy::expect_used, clippy::diverging_sub_expression, clippy::needless_return)]
|
||||
{
|
||||
return #rt
|
||||
.enable_all()
|
||||
@@ -402,7 +469,7 @@ fn parse_knobs(mut input: ItemFn, is_test: bool, config: FinalConfig) -> TokenSt
|
||||
quote! {
|
||||
let body = async #body;
|
||||
#crate_path::pin!(body);
|
||||
let body: ::std::pin::Pin<&mut dyn ::std::future::Future<Output = #output_type>> = body;
|
||||
let body: ::core::pin::Pin<&mut dyn ::core::future::Future<Output = #output_type>> = body;
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
@@ -410,7 +477,7 @@ fn parse_knobs(mut input: ItemFn, is_test: bool, config: FinalConfig) -> TokenSt
|
||||
}
|
||||
};
|
||||
|
||||
input.into_tokens(header, body, last_block)
|
||||
input.into_tokens(generated_attrs, body, last_block)
|
||||
}
|
||||
|
||||
fn token_stream_with_error(mut tokens: TokenStream, error: syn::Error) -> TokenStream {
|
||||
@@ -418,7 +485,6 @@ fn token_stream_with_error(mut tokens: TokenStream, error: syn::Error) -> TokenS
|
||||
tokens
|
||||
}
|
||||
|
||||
#[cfg(not(test))] // Work around for rust-lang/rust#62127
|
||||
pub(crate) fn main(args: TokenStream, item: TokenStream, rt_multi_thread: bool) -> TokenStream {
|
||||
// If any of the steps for this macro fail, we still want to expand to an item that is as close
|
||||
// to the expected output as possible. This helps out IDEs such that completions and other
|
||||
@@ -443,6 +509,35 @@ pub(crate) fn main(args: TokenStream, item: TokenStream, rt_multi_thread: bool)
|
||||
}
|
||||
}
|
||||
|
||||
// Check whether given attribute is a test attribute of forms:
|
||||
// * `#[test]`
|
||||
// * `#[core::prelude::*::test]` or `#[::core::prelude::*::test]`
|
||||
// * `#[std::prelude::*::test]` or `#[::std::prelude::*::test]`
|
||||
fn is_test_attribute(attr: &Attribute) -> bool {
|
||||
let path = match &attr.meta {
|
||||
syn::Meta::Path(path) => path,
|
||||
_ => return false,
|
||||
};
|
||||
let candidates = [
|
||||
["core", "prelude", "*", "test"],
|
||||
["std", "prelude", "*", "test"],
|
||||
];
|
||||
if path.leading_colon.is_none()
|
||||
&& path.segments.len() == 1
|
||||
&& path.segments[0].arguments.is_none()
|
||||
&& path.segments[0].ident == "test"
|
||||
{
|
||||
return true;
|
||||
} else if path.segments.len() != candidates[0].len() {
|
||||
return false;
|
||||
}
|
||||
candidates.into_iter().any(|segments| {
|
||||
path.segments.iter().zip(segments).all(|(segment, path)| {
|
||||
segment.arguments.is_none() && (path == "*" || segment.ident == path)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn test(args: TokenStream, item: TokenStream, rt_multi_thread: bool) -> TokenStream {
|
||||
// If any of the steps for this macro fail, we still want to expand to an item that is as close
|
||||
// to the expected output as possible. This helps out IDEs such that completions and other
|
||||
@@ -451,8 +546,8 @@ pub(crate) fn test(args: TokenStream, item: TokenStream, rt_multi_thread: bool)
|
||||
Ok(it) => it,
|
||||
Err(e) => return token_stream_with_error(item, e),
|
||||
};
|
||||
let config = if let Some(attr) = input.attrs().find(|attr| attr.meta.path().is_ident("test")) {
|
||||
let msg = "second test attribute is supplied";
|
||||
let config = if let Some(attr) = input.attrs().find(|attr| is_test_attribute(attr)) {
|
||||
let msg = "second test attribute is supplied, consider removing or changing the order of your test attributes";
|
||||
Err(syn::Error::new_spanned(attr, msg))
|
||||
} else {
|
||||
AttributeArgs::parse_terminated
|
||||
@@ -493,13 +588,11 @@ impl ItemFn {
|
||||
/// Convert our local function item into a token stream.
|
||||
fn into_tokens(
|
||||
self,
|
||||
header: proc_macro2::TokenStream,
|
||||
generated_attrs: proc_macro2::TokenStream,
|
||||
body: proc_macro2::TokenStream,
|
||||
last_block: proc_macro2::TokenStream,
|
||||
) -> TokenStream {
|
||||
let mut tokens = proc_macro2::TokenStream::new();
|
||||
header.to_tokens(&mut tokens);
|
||||
|
||||
// Outer attributes are simply streamed as-is.
|
||||
for attr in self.outer_attrs {
|
||||
attr.to_tokens(&mut tokens);
|
||||
@@ -507,12 +600,15 @@ impl ItemFn {
|
||||
|
||||
// Inner attributes require extra care, since they're not supported on
|
||||
// blocks (which is what we're expanded into) we instead lift them
|
||||
// outside of the function. This matches the behaviour of `syn`.
|
||||
// outside of the function. This matches the behavior of `syn`.
|
||||
for mut attr in self.inner_attrs {
|
||||
attr.style = syn::AttrStyle::Outer;
|
||||
attr.to_tokens(&mut tokens);
|
||||
}
|
||||
|
||||
// Add generated macros at the end, so macros processed later are aware of them.
|
||||
generated_attrs.to_tokens(&mut tokens);
|
||||
|
||||
self.vis.to_tokens(&mut tokens);
|
||||
self.sig.to_tokens(&mut tokens);
|
||||
|
||||
@@ -586,6 +682,6 @@ impl ToTokens for Body<'_> {
|
||||
for stmt in self.stmts {
|
||||
stmt.to_tokens(tokens);
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+94
-3
@@ -201,8 +201,53 @@ use proc_macro::TokenStream;
|
||||
/// })
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ### Configure unhandled panic behavior
|
||||
///
|
||||
/// Available options are `shutdown_runtime` and `ignore`. For more details, see
|
||||
/// [`Builder::unhandled_panic`].
|
||||
///
|
||||
/// This option is only compatible with the `current_thread` runtime.
|
||||
///
|
||||
/// ```no_run
|
||||
/// #[cfg(tokio_unstable)]
|
||||
/// #[tokio::main(flavor = "current_thread", unhandled_panic = "shutdown_runtime")]
|
||||
/// async fn main() {
|
||||
/// let _ = tokio::spawn(async {
|
||||
/// panic!("This panic will shutdown the runtime.");
|
||||
/// }).await;
|
||||
/// }
|
||||
/// # #[cfg(not(tokio_unstable))]
|
||||
/// # fn main() { }
|
||||
/// ```
|
||||
///
|
||||
/// Equivalent code not using `#[tokio::main]`
|
||||
///
|
||||
/// ```no_run
|
||||
/// #[cfg(tokio_unstable)]
|
||||
/// fn main() {
|
||||
/// tokio::runtime::Builder::new_current_thread()
|
||||
/// .enable_all()
|
||||
/// .unhandled_panic(UnhandledPanic::ShutdownRuntime)
|
||||
/// .build()
|
||||
/// .unwrap()
|
||||
/// .block_on(async {
|
||||
/// let _ = tokio::spawn(async {
|
||||
/// panic!("This panic will shutdown the runtime.");
|
||||
/// }).await;
|
||||
/// })
|
||||
/// }
|
||||
/// # #[cfg(not(tokio_unstable))]
|
||||
/// # fn main() { }
|
||||
/// ```
|
||||
///
|
||||
/// **Note**: This option depends on Tokio's [unstable API][unstable]. See [the
|
||||
/// documentation on unstable features][unstable] for details on how to enable
|
||||
/// Tokio's unstable features.
|
||||
///
|
||||
/// [`Builder::unhandled_panic`]: ../tokio/runtime/struct.Builder.html#method.unhandled_panic
|
||||
/// [unstable]: ../tokio/index.html#unstable-features
|
||||
#[proc_macro_attribute]
|
||||
#[cfg(not(test))] // Work around for rust-lang/rust#62127
|
||||
pub fn main(args: TokenStream, item: TokenStream) -> TokenStream {
|
||||
entry::main(args.into(), item.into(), true).into()
|
||||
}
|
||||
@@ -267,7 +312,6 @@ pub fn main(args: TokenStream, item: TokenStream) -> TokenStream {
|
||||
/// }
|
||||
/// ```
|
||||
#[proc_macro_attribute]
|
||||
#[cfg(not(test))] // Work around for rust-lang/rust#62127
|
||||
pub fn main_rt(args: TokenStream, item: TokenStream) -> TokenStream {
|
||||
entry::main(args.into(), item.into(), false).into()
|
||||
}
|
||||
@@ -365,7 +409,7 @@ pub fn main_rt(args: TokenStream, item: TokenStream) -> TokenStream {
|
||||
/// ### Set number of worker threads
|
||||
///
|
||||
/// ```no_run
|
||||
/// #[tokio::test(flavor ="multi_thread", worker_threads = 2)]
|
||||
/// #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
/// async fn my_test() {
|
||||
/// assert!(true);
|
||||
/// }
|
||||
@@ -424,6 +468,53 @@ pub fn main_rt(args: TokenStream, item: TokenStream) -> TokenStream {
|
||||
/// println!("Hello world");
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ### Configure unhandled panic behavior
|
||||
///
|
||||
/// Available options are `shutdown_runtime` and `ignore`. For more details, see
|
||||
/// [`Builder::unhandled_panic`].
|
||||
///
|
||||
/// This option is only compatible with the `current_thread` runtime.
|
||||
///
|
||||
/// ```no_run
|
||||
/// #[cfg(tokio_unstable)]
|
||||
/// #[tokio::test(flavor = "current_thread", unhandled_panic = "shutdown_runtime")]
|
||||
/// async fn my_test() {
|
||||
/// let _ = tokio::spawn(async {
|
||||
/// panic!("This panic will shutdown the runtime.");
|
||||
/// }).await;
|
||||
/// }
|
||||
/// # #[cfg(not(tokio_unstable))]
|
||||
/// # fn main() { }
|
||||
/// ```
|
||||
///
|
||||
/// Equivalent code not using `#[tokio::test]`
|
||||
///
|
||||
/// ```no_run
|
||||
/// #[cfg(tokio_unstable)]
|
||||
/// #[test]
|
||||
/// fn my_test() {
|
||||
/// tokio::runtime::Builder::new_current_thread()
|
||||
/// .enable_all()
|
||||
/// .unhandled_panic(UnhandledPanic::ShutdownRuntime)
|
||||
/// .build()
|
||||
/// .unwrap()
|
||||
/// .block_on(async {
|
||||
/// let _ = tokio::spawn(async {
|
||||
/// panic!("This panic will shutdown the runtime.");
|
||||
/// }).await;
|
||||
/// })
|
||||
/// }
|
||||
/// # #[cfg(not(tokio_unstable))]
|
||||
/// # fn main() { }
|
||||
/// ```
|
||||
///
|
||||
/// **Note**: This option depends on Tokio's [unstable API][unstable]. See [the
|
||||
/// documentation on unstable features][unstable] for details on how to enable
|
||||
/// Tokio's unstable features.
|
||||
///
|
||||
/// [`Builder::unhandled_panic`]: ../tokio/runtime/struct.Builder.html#method.unhandled_panic
|
||||
/// [unstable]: ../tokio/index.html#unstable-features
|
||||
#[proc_macro_attribute]
|
||||
pub fn test(args: TokenStream, item: TokenStream) -> TokenStream {
|
||||
entry::test(args.into(), item.into(), true).into()
|
||||
|
||||
@@ -11,7 +11,7 @@ pub(crate) fn declare_output_enum(input: TokenStream) -> TokenStream {
|
||||
};
|
||||
|
||||
let variants = (0..branches)
|
||||
.map(|num| Ident::new(&format!("_{}", num), Span::call_site()))
|
||||
.map(|num| Ident::new(&format!("_{num}"), Span::call_site()))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Use a bitfield to track which futures completed
|
||||
@@ -73,27 +73,27 @@ fn clean_pattern(pat: &mut syn::Pat) {
|
||||
}
|
||||
}
|
||||
syn::Pat::Or(or) => {
|
||||
for case in or.cases.iter_mut() {
|
||||
for case in &mut or.cases {
|
||||
clean_pattern(case);
|
||||
}
|
||||
}
|
||||
syn::Pat::Slice(slice) => {
|
||||
for elem in slice.elems.iter_mut() {
|
||||
for elem in &mut slice.elems {
|
||||
clean_pattern(elem);
|
||||
}
|
||||
}
|
||||
syn::Pat::Struct(struct_pat) => {
|
||||
for field in struct_pat.fields.iter_mut() {
|
||||
for field in &mut struct_pat.fields {
|
||||
clean_pattern(&mut field.pat);
|
||||
}
|
||||
}
|
||||
syn::Pat::Tuple(tuple) => {
|
||||
for elem in tuple.elems.iter_mut() {
|
||||
for elem in &mut tuple.elems {
|
||||
clean_pattern(elem);
|
||||
}
|
||||
}
|
||||
syn::Pat::TupleStruct(tuple) => {
|
||||
for elem in tuple.elems.iter_mut() {
|
||||
for elem in &mut tuple.elems {
|
||||
clean_pattern(elem);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,42 @@
|
||||
# 0.1.17 (December 6th, 2024)
|
||||
|
||||
- deps: fix dev-dependency on tokio-test ([#6931], [#7019])
|
||||
- stream: fix link on `Peekable` ([#6861])
|
||||
- sync: fix `Stream` link in broadcast docs ([#6873])
|
||||
|
||||
[#6861]: https://github.com/tokio-rs/tokio/pull/6861
|
||||
[#6873]: https://github.com/tokio-rs/tokio/pull/6873
|
||||
[#6931]: https://github.com/tokio-rs/tokio/pull/6931
|
||||
[#7019]: https://github.com/tokio-rs/tokio/pull/7019
|
||||
|
||||
# 0.1.16 (September 5th, 2024)
|
||||
|
||||
This release bumps the MSRV of tokio-stream to 1.70.
|
||||
|
||||
- stream: add `next_many` and `poll_next_many` to `StreamMap` ([#6409])
|
||||
- stream: make stream adapters public ([#6658])
|
||||
- readme: add readme for tokio-stream ([#6456])
|
||||
|
||||
[#6409]: https://github.com/tokio-rs/tokio/pull/6409
|
||||
[#6658]: https://github.com/tokio-rs/tokio/pull/6658
|
||||
[#6456]: https://github.com/tokio-rs/tokio/pull/6456
|
||||
|
||||
# 0.1.15 (March 14th, 2024)
|
||||
|
||||
This release bumps the MSRV of tokio-stream to 1.63.
|
||||
|
||||
- docs: fix typo in argument name ([#6389])
|
||||
- docs: fix typo in peekable docs ([#6130])
|
||||
- docs: link to latest version of tokio-util docs ([#5694])
|
||||
- docs: typographic improvements ([#6262])
|
||||
- stream: add `StreamExt::peekable` ([#6095])
|
||||
|
||||
[#5694]: https://github.com/tokio-rs/tokio/pull/5694
|
||||
[#6095]: https://github.com/tokio-rs/tokio/pull/6095
|
||||
[#6130]: https://github.com/tokio-rs/tokio/pull/6130
|
||||
[#6262]: https://github.com/tokio-rs/tokio/pull/6262
|
||||
[#6389]: https://github.com/tokio-rs/tokio/pull/6389
|
||||
|
||||
# 0.1.14 (April 26th, 2023)
|
||||
|
||||
This bugfix release bumps the minimum version of Tokio to 1.15, which is
|
||||
|
||||
@@ -4,9 +4,9 @@ name = "tokio-stream"
|
||||
# - Remove path dependencies
|
||||
# - Update CHANGELOG.md.
|
||||
# - Create "tokio-stream-0.1.x" git tag.
|
||||
version = "0.1.14"
|
||||
version = "0.1.17"
|
||||
edition = "2021"
|
||||
rust-version = "1.56"
|
||||
rust-version = "1.70"
|
||||
authors = ["Tokio Contributors <[email protected]>"]
|
||||
license = "MIT"
|
||||
repository = "https://github.com/tokio-rs/tokio"
|
||||
@@ -45,7 +45,7 @@ tokio-util = { version = "0.7.0", path = "../tokio-util", optional = true }
|
||||
tokio = { version = "1.2.0", path = "../tokio", features = ["full", "test-util"] }
|
||||
async-stream = "0.3"
|
||||
parking_lot = "0.12.0"
|
||||
tokio-test = { path = "../tokio-test" }
|
||||
tokio-test = { version = "0.4", path = "../tokio-test" }
|
||||
futures = { version = "0.3", default-features = false }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
@@ -56,3 +56,6 @@ rustdoc-args = ["--cfg", "docsrs"]
|
||||
# This should allow `docsrs` to be read across projects, so that `tokio-stream`
|
||||
# can pick up stubbed types exported by `tokio`.
|
||||
rustc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
+18
-22
@@ -1,25 +1,21 @@
|
||||
Copyright (c) 2023 Tokio Contributors
|
||||
MIT License
|
||||
|
||||
Permission is hereby granted, free of charge, to any
|
||||
person obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the
|
||||
Software without restriction, including without
|
||||
limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software
|
||||
is furnished to do so, subject to the following
|
||||
conditions:
|
||||
Copyright (c) Tokio Contributors
|
||||
|
||||
The above copyright notice and this permission notice
|
||||
shall be included in all copies or substantial portions
|
||||
of the Software.
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
# tokio-stream
|
||||
|
||||
Utilities to work with `Stream` and `tokio`.
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the [MIT license](LICENSE).
|
||||
|
||||
### Contribution
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted
|
||||
for inclusion in Tokio by you, shall be licensed as MIT, without any additional
|
||||
terms or conditions.
|
||||
+16
-2
@@ -54,7 +54,7 @@
|
||||
//!
|
||||
//! [async-stream]: https://docs.rs/async-stream
|
||||
//!
|
||||
//! # Conversion to and from AsyncRead/AsyncWrite
|
||||
//! # Conversion to and from `AsyncRead`/`AsyncWrite`
|
||||
//!
|
||||
//! It is often desirable to convert a `Stream` into an [`AsyncRead`],
|
||||
//! especially when dealing with plaintext formats streamed over the network.
|
||||
@@ -77,8 +77,22 @@ pub mod wrappers;
|
||||
|
||||
mod stream_ext;
|
||||
pub use stream_ext::{collect::FromStream, StreamExt};
|
||||
/// Adapters for [`Stream`]s created by methods in [`StreamExt`].
|
||||
pub mod adapters {
|
||||
pub use crate::stream_ext::{
|
||||
Chain, Filter, FilterMap, Fuse, Map, MapWhile, Merge, Peekable, Skip, SkipWhile, Take,
|
||||
TakeWhile, Then,
|
||||
};
|
||||
cfg_time! {
|
||||
pub use crate::stream_ext::{ChunksTimeout, Timeout, TimeoutRepeating};
|
||||
}
|
||||
}
|
||||
|
||||
cfg_time! {
|
||||
pub use stream_ext::timeout::{Elapsed, Timeout};
|
||||
#[deprecated = "Import those symbols from adapters instead"]
|
||||
#[doc(hidden)]
|
||||
pub use stream_ext::timeout::Timeout;
|
||||
pub use stream_ext::timeout::Elapsed;
|
||||
}
|
||||
|
||||
mod empty;
|
||||
|
||||
@@ -57,12 +57,3 @@ macro_rules! cfg_signal {
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! ready {
|
||||
($e:expr $(,)?) => {
|
||||
match $e {
|
||||
std::task::Poll::Ready(t) => t,
|
||||
std::task::Poll::Pending => return std::task::Poll::Pending,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ impl<I> Unpin for Once<I> {}
|
||||
/// ```
|
||||
pub fn once<T>(value: T) -> Once<T> {
|
||||
Once {
|
||||
iter: crate::iter(Some(value).into_iter()),
|
||||
iter: crate::iter(Some(value)),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ unsafe impl<T> Sync for Pending<T> {}
|
||||
///
|
||||
/// The returned stream is never ready. Attempting to call
|
||||
/// [`next()`](crate::StreamExt::next) will never complete. Use
|
||||
/// [`stream::empty()`](super::empty()) to obtain a stream that is is
|
||||
/// [`stream::empty()`](super::empty()) to obtain a stream that is
|
||||
/// immediately empty but returns no values.
|
||||
///
|
||||
/// # Examples
|
||||
|
||||
@@ -8,63 +8,66 @@ mod any;
|
||||
use any::AnyFuture;
|
||||
|
||||
mod chain;
|
||||
use chain::Chain;
|
||||
pub use chain::Chain;
|
||||
|
||||
pub(crate) mod collect;
|
||||
use collect::{Collect, FromStream};
|
||||
|
||||
mod filter;
|
||||
use filter::Filter;
|
||||
pub use filter::Filter;
|
||||
|
||||
mod filter_map;
|
||||
use filter_map::FilterMap;
|
||||
pub use filter_map::FilterMap;
|
||||
|
||||
mod fold;
|
||||
use fold::FoldFuture;
|
||||
|
||||
mod fuse;
|
||||
use fuse::Fuse;
|
||||
pub use fuse::Fuse;
|
||||
|
||||
mod map;
|
||||
use map::Map;
|
||||
pub use map::Map;
|
||||
|
||||
mod map_while;
|
||||
use map_while::MapWhile;
|
||||
pub use map_while::MapWhile;
|
||||
|
||||
mod merge;
|
||||
use merge::Merge;
|
||||
pub use merge::Merge;
|
||||
|
||||
mod next;
|
||||
use next::Next;
|
||||
|
||||
mod skip;
|
||||
use skip::Skip;
|
||||
pub use skip::Skip;
|
||||
|
||||
mod skip_while;
|
||||
use skip_while::SkipWhile;
|
||||
pub use skip_while::SkipWhile;
|
||||
|
||||
mod take;
|
||||
use take::Take;
|
||||
pub use take::Take;
|
||||
|
||||
mod take_while;
|
||||
use take_while::TakeWhile;
|
||||
pub use take_while::TakeWhile;
|
||||
|
||||
mod then;
|
||||
use then::Then;
|
||||
pub use then::Then;
|
||||
|
||||
mod try_next;
|
||||
use try_next::TryNext;
|
||||
|
||||
mod peekable;
|
||||
pub use peekable::Peekable;
|
||||
|
||||
cfg_time! {
|
||||
pub(crate) mod timeout;
|
||||
pub(crate) mod timeout_repeating;
|
||||
use timeout::Timeout;
|
||||
use timeout_repeating::TimeoutRepeating;
|
||||
pub use timeout::Timeout;
|
||||
pub use timeout_repeating::TimeoutRepeating;
|
||||
use tokio::time::{Duration, Interval};
|
||||
mod throttle;
|
||||
use throttle::{throttle, Throttle};
|
||||
mod chunks_timeout;
|
||||
use chunks_timeout::ChunksTimeout;
|
||||
pub use chunks_timeout::ChunksTimeout;
|
||||
}
|
||||
|
||||
/// An extension trait for the [`Stream`] trait that provides a variety of
|
||||
@@ -848,8 +851,7 @@ pub trait StreamExt: Stream {
|
||||
///
|
||||
/// `collect` streams all values, awaiting as needed. Values are pushed into
|
||||
/// a collection. A number of different target collection types are
|
||||
/// supported, including [`Vec`](std::vec::Vec),
|
||||
/// [`String`](std::string::String), and [`Bytes`].
|
||||
/// supported, including [`Vec`], [`String`], and [`Bytes`].
|
||||
///
|
||||
/// [`Bytes`]: https://docs.rs/bytes/0.6.0/bytes/struct.Bytes.html
|
||||
///
|
||||
@@ -1005,9 +1007,8 @@ pub trait StreamExt: Stream {
|
||||
|
||||
/// Applies a per-item timeout to the passed stream.
|
||||
///
|
||||
/// `timeout_repeating()` takes an [`Interval`](tokio::time::Interval) that
|
||||
/// controls the time each element of the stream has to complete before
|
||||
/// timing out.
|
||||
/// `timeout_repeating()` takes an [`Interval`] that controls the time each
|
||||
/// element of the stream has to complete before timing out.
|
||||
///
|
||||
/// If the wrapped stream yields a value before the deadline is reached, the
|
||||
/// value is returned. Otherwise, an error is returned. The caller may decide
|
||||
@@ -1178,6 +1179,31 @@ pub trait StreamExt: Stream {
|
||||
assert!(max_size > 0, "`max_size` must be non-zero.");
|
||||
ChunksTimeout::new(self, max_size, duration)
|
||||
}
|
||||
|
||||
/// Turns the stream into a peekable stream, whose next element can be peeked at without being
|
||||
/// consumed.
|
||||
/// ```rust
|
||||
/// use tokio_stream::{self as stream, StreamExt};
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// # async fn _unused() {}
|
||||
/// # #[tokio::main(flavor = "current_thread", start_paused = true)]
|
||||
/// async fn main() {
|
||||
/// let iter = vec![1, 2, 3, 4].into_iter();
|
||||
/// let mut stream = stream::iter(iter).peekable();
|
||||
///
|
||||
/// assert_eq!(*stream.peek().await.unwrap(), 1);
|
||||
/// assert_eq!(*stream.peek().await.unwrap(), 1);
|
||||
/// assert_eq!(stream.next().await.unwrap(), 1);
|
||||
/// assert_eq!(*stream.peek().await.unwrap(), 2);
|
||||
/// }
|
||||
/// ```
|
||||
fn peekable(self) -> Peekable<Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Peekable::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<St: ?Sized> StreamExt for St where St: Stream {}
|
||||
|
||||
@@ -3,7 +3,7 @@ use crate::Stream;
|
||||
use core::future::Future;
|
||||
use core::marker::PhantomPinned;
|
||||
use core::pin::Pin;
|
||||
use core::task::{Context, Poll};
|
||||
use core::task::{ready, Context, Poll};
|
||||
use pin_project_lite::pin_project;
|
||||
|
||||
pin_project! {
|
||||
@@ -42,7 +42,7 @@ where
|
||||
|
||||
// Take a maximum of 32 items from the stream before yielding.
|
||||
for _ in 0..32 {
|
||||
match futures_core::ready!(stream.as_mut().poll_next(cx)) {
|
||||
match ready!(stream.as_mut().poll_next(cx)) {
|
||||
Some(v) => {
|
||||
if !(me.f)(v) {
|
||||
return Poll::Ready(false);
|
||||
|
||||
@@ -3,7 +3,7 @@ use crate::Stream;
|
||||
use core::future::Future;
|
||||
use core::marker::PhantomPinned;
|
||||
use core::pin::Pin;
|
||||
use core::task::{Context, Poll};
|
||||
use core::task::{ready, Context, Poll};
|
||||
use pin_project_lite::pin_project;
|
||||
|
||||
pin_project! {
|
||||
@@ -42,7 +42,7 @@ where
|
||||
|
||||
// Take a maximum of 32 items from the stream before yielding.
|
||||
for _ in 0..32 {
|
||||
match futures_core::ready!(stream.as_mut().poll_next(cx)) {
|
||||
match ready!(stream.as_mut().poll_next(cx)) {
|
||||
Some(v) => {
|
||||
if (me.f)(v) {
|
||||
return Poll::Ready(true);
|
||||
|
||||
@@ -2,7 +2,7 @@ use crate::stream_ext::Fuse;
|
||||
use crate::Stream;
|
||||
|
||||
use core::pin::Pin;
|
||||
use core::task::{Context, Poll};
|
||||
use core::task::{ready, Context, Poll};
|
||||
use pin_project_lite::pin_project;
|
||||
|
||||
pin_project! {
|
||||
|
||||
@@ -4,7 +4,7 @@ use tokio::time::{sleep, Sleep};
|
||||
|
||||
use core::future::Future;
|
||||
use core::pin::Pin;
|
||||
use core::task::{Context, Poll};
|
||||
use core::task::{ready, Context, Poll};
|
||||
use pin_project_lite::pin_project;
|
||||
use std::time::Duration;
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ use core::future::Future;
|
||||
use core::marker::PhantomPinned;
|
||||
use core::mem;
|
||||
use core::pin::Pin;
|
||||
use core::task::{Context, Poll};
|
||||
use core::task::{ready, Context, Poll};
|
||||
use pin_project_lite::pin_project;
|
||||
|
||||
// Do not export this struct until `FromStream` can be unsealed.
|
||||
@@ -26,7 +26,7 @@ pin_project! {
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert from a [`Stream`](crate::Stream).
|
||||
/// Convert from a [`Stream`].
|
||||
///
|
||||
/// This trait is not intended to be used directly. Instead, call
|
||||
/// [`StreamExt::collect()`](super::StreamExt::collect).
|
||||
|
||||
@@ -2,7 +2,7 @@ use crate::Stream;
|
||||
|
||||
use core::fmt;
|
||||
use core::pin::Pin;
|
||||
use core::task::{Context, Poll};
|
||||
use core::task::{ready, Context, Poll};
|
||||
use pin_project_lite::pin_project;
|
||||
|
||||
pin_project! {
|
||||
|
||||
@@ -2,7 +2,7 @@ use crate::Stream;
|
||||
|
||||
use core::fmt;
|
||||
use core::pin::Pin;
|
||||
use core::task::{Context, Poll};
|
||||
use core::task::{ready, Context, Poll};
|
||||
use pin_project_lite::pin_project;
|
||||
|
||||
pin_project! {
|
||||
|
||||
@@ -3,7 +3,7 @@ use crate::Stream;
|
||||
use core::future::Future;
|
||||
use core::marker::PhantomPinned;
|
||||
use core::pin::Pin;
|
||||
use core::task::{Context, Poll};
|
||||
use core::task::{ready, Context, Poll};
|
||||
use pin_project_lite::pin_project;
|
||||
|
||||
pin_project! {
|
||||
|
||||
@@ -2,7 +2,7 @@ use crate::Stream;
|
||||
|
||||
use pin_project_lite::pin_project;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
use std::task::{ready, Context, Poll};
|
||||
|
||||
pin_project! {
|
||||
/// Stream returned by [`fuse()`][super::StreamExt::fuse].
|
||||
|
||||
@@ -66,25 +66,23 @@ where
|
||||
T: Stream,
|
||||
U: Stream<Item = T::Item>,
|
||||
{
|
||||
use Poll::*;
|
||||
|
||||
let mut done = true;
|
||||
|
||||
match first.poll_next(cx) {
|
||||
Ready(Some(val)) => return Ready(Some(val)),
|
||||
Ready(None) => {}
|
||||
Pending => done = false,
|
||||
Poll::Ready(Some(val)) => return Poll::Ready(Some(val)),
|
||||
Poll::Ready(None) => {}
|
||||
Poll::Pending => done = false,
|
||||
}
|
||||
|
||||
match second.poll_next(cx) {
|
||||
Ready(Some(val)) => return Ready(Some(val)),
|
||||
Ready(None) => {}
|
||||
Pending => done = false,
|
||||
Poll::Ready(Some(val)) => return Poll::Ready(Some(val)),
|
||||
Poll::Ready(None) => {}
|
||||
Poll::Pending => done = false,
|
||||
}
|
||||
|
||||
if done {
|
||||
Ready(None)
|
||||
Poll::Ready(None)
|
||||
} else {
|
||||
Pending
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
use futures_core::Stream;
|
||||
use pin_project_lite::pin_project;
|
||||
|
||||
use crate::stream_ext::Fuse;
|
||||
use crate::StreamExt;
|
||||
|
||||
pin_project! {
|
||||
/// Stream returned by the [`peekable`](super::StreamExt::peekable) method.
|
||||
pub struct Peekable<T: Stream> {
|
||||
peek: Option<T::Item>,
|
||||
#[pin]
|
||||
stream: Fuse<T>,
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Stream> Peekable<T> {
|
||||
pub(crate) fn new(stream: T) -> Self {
|
||||
let stream = stream.fuse();
|
||||
Self { peek: None, stream }
|
||||
}
|
||||
|
||||
/// Peek at the next item in the stream.
|
||||
pub async fn peek(&mut self) -> Option<&T::Item>
|
||||
where
|
||||
T: Unpin,
|
||||
{
|
||||
if let Some(ref it) = self.peek {
|
||||
Some(it)
|
||||
} else {
|
||||
self.peek = self.next().await;
|
||||
self.peek.as_ref()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Stream> Stream for Peekable<T> {
|
||||
type Item = T::Item;
|
||||
|
||||
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||
let this = self.project();
|
||||
if let Some(it) = this.peek.take() {
|
||||
Poll::Ready(Some(it))
|
||||
} else {
|
||||
this.stream.poll_next(cx)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@ use crate::Stream;
|
||||
|
||||
use core::fmt;
|
||||
use core::pin::Pin;
|
||||
use core::task::{Context, Poll};
|
||||
use core::task::{ready, Context, Poll};
|
||||
use pin_project_lite::pin_project;
|
||||
|
||||
pin_project! {
|
||||
|
||||
@@ -2,7 +2,7 @@ use crate::Stream;
|
||||
|
||||
use core::fmt;
|
||||
use core::pin::Pin;
|
||||
use core::task::{Context, Poll};
|
||||
use core::task::{ready, Context, Poll};
|
||||
use pin_project_lite::pin_project;
|
||||
|
||||
pin_project! {
|
||||
|
||||
@@ -5,7 +5,7 @@ use tokio::time::{Duration, Instant, Sleep};
|
||||
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
use std::task::{self, Poll};
|
||||
use std::task::{self, ready, Poll};
|
||||
|
||||
use pin_project_lite::pin_project;
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ use tokio::time::{Instant, Sleep};
|
||||
|
||||
use core::future::Future;
|
||||
use core::pin::Pin;
|
||||
use core::task::{Context, Poll};
|
||||
use core::task::{ready, Context, Poll};
|
||||
use pin_project_lite::pin_project;
|
||||
use std::fmt;
|
||||
use std::time::Duration;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user