├── .github └── workflows │ └── ci.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── macros ├── Cargo.toml ├── LICENSE └── src │ └── lib.rs └── src └── lib.rs /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Main CI 2 | on: 3 | pull_request: 4 | branches: ["**"] 5 | push: 6 | branches: 7 | - main 8 | 9 | jobs: 10 | lint: 11 | name: Check and Lint 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | - uses: actions-rs/toolchain@v1 16 | with: 17 | profile: minimal 18 | toolchain: stable 19 | override: true 20 | components: clippy 21 | - name: Cache cargo dependencies 22 | uses: actions/cache@v2 23 | with: 24 | path: ~/.cargo/ 25 | key: cache-cargo-deps-${{ runner.os }}-${{ hashFiles('**/Cargo.lock', '**/Cargo.toml') }} 26 | - name: Check 27 | uses: actions-rs/cargo@v1 28 | with: 29 | command: clippy 30 | args: --all --all-targets --all-features -- --deny warnings 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "async-dispatcher" 7 | version = "0.1.2" 8 | dependencies = [ 9 | "async-dispatcher-macros", 10 | "async-task", 11 | "futures-lite", 12 | ] 13 | 14 | [[package]] 15 | name = "async-dispatcher-macros" 16 | version = "0.1.0" 17 | dependencies = [ 18 | "proc-macro2", 19 | "quote", 20 | "syn", 21 | ] 22 | 23 | [[package]] 24 | name = "async-task" 25 | version = "4.7.1" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" 28 | 29 | [[package]] 30 | name = "cfg-if" 31 | version = "1.0.0" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 34 | 35 | [[package]] 36 | name = "fastrand" 37 | version = "1.9.0" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" 40 | dependencies = [ 41 | "instant", 42 | ] 43 | 44 | [[package]] 45 | name = "futures-core" 46 | version = "0.3.30" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" 49 | 50 | [[package]] 51 | name = "futures-io" 52 | version = "0.3.30" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" 55 | 56 | [[package]] 57 | name = "futures-lite" 58 | version = "1.13.0" 59 | source = "registry+https://github.com/rust-lang/crates.io-index" 60 | checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" 61 | dependencies = [ 62 | "fastrand", 63 | "futures-core", 64 | "futures-io", 65 | "memchr", 66 | "parking", 67 | "pin-project-lite", 68 | "waker-fn", 69 | ] 70 | 71 | [[package]] 72 | name = "instant" 73 | version = "0.1.13" 74 | source = "registry+https://github.com/rust-lang/crates.io-index" 75 | checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" 76 | dependencies = [ 77 | "cfg-if", 78 | ] 79 | 80 | [[package]] 81 | name = "memchr" 82 | version = "2.7.2" 83 | source = "registry+https://github.com/rust-lang/crates.io-index" 84 | checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" 85 | 86 | [[package]] 87 | name = "parking" 88 | version = "2.2.0" 89 | source = "registry+https://github.com/rust-lang/crates.io-index" 90 | checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" 91 | 92 | [[package]] 93 | name = "pin-project-lite" 94 | version = "0.2.14" 95 | source = "registry+https://github.com/rust-lang/crates.io-index" 96 | checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" 97 | 98 | [[package]] 99 | name = "proc-macro2" 100 | version = "1.0.84" 101 | source = "registry+https://github.com/rust-lang/crates.io-index" 102 | checksum = "ec96c6a92621310b51366f1e28d05ef11489516e93be030060e5fc12024a49d6" 103 | dependencies = [ 104 | "unicode-ident", 105 | ] 106 | 107 | [[package]] 108 | name = "quote" 109 | version = "1.0.36" 110 | source = "registry+https://github.com/rust-lang/crates.io-index" 111 | checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" 112 | dependencies = [ 113 | "proc-macro2", 114 | ] 115 | 116 | [[package]] 117 | name = "syn" 118 | version = "2.0.66" 119 | source = "registry+https://github.com/rust-lang/crates.io-index" 120 | checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" 121 | dependencies = [ 122 | "proc-macro2", 123 | "quote", 124 | "unicode-ident", 125 | ] 126 | 127 | [[package]] 128 | name = "unicode-ident" 129 | version = "1.0.12" 130 | source = "registry+https://github.com/rust-lang/crates.io-index" 131 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 132 | 133 | [[package]] 134 | name = "waker-fn" 135 | version = "1.2.0" 136 | source = "registry+https://github.com/rust-lang/crates.io-index" 137 | checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7" 138 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "async-dispatcher" 3 | version = "0.1.2" 4 | edition = "2021" 5 | license = "Apache-2.0" 6 | description = "async runtime based on a pluggable dispatcher" 7 | 8 | [features] 9 | "macros" = ["async-dispatcher-macros"] 10 | 11 | [dependencies] 12 | async-task = "4.7" 13 | futures-lite = "1" 14 | async-dispatcher-macros = { path = "./macros", version = "0.1.0", optional = true } 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2024 Zed Industries, Inc. 2 | 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | 18 | 19 | 20 | 21 | Apache License 22 | Version 2.0, January 2004 23 | http://www.apache.org/licenses/ 24 | 25 | 26 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 27 | 28 | 29 | 1. Definitions. 30 | 31 | 32 | "License" shall mean the terms and conditions for use, reproduction, 33 | and distribution as defined by Sections 1 through 9 of this document. 34 | 35 | 36 | "Licensor" shall mean the copyright owner or entity authorized by 37 | the copyright owner that is granting the License. 38 | 39 | 40 | "Legal Entity" shall mean the union of the acting entity and all 41 | other entities that control, are controlled by, or are under common 42 | control with that entity. For the purposes of this definition, 43 | "control" means (i) the power, direct or indirect, to cause the 44 | direction or management of such entity, whether by contract or 45 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 46 | outstanding shares, or (iii) beneficial ownership of such entity. 47 | 48 | 49 | "You" (or "Your") shall mean an individual or Legal Entity 50 | exercising permissions granted by this License. 51 | 52 | 53 | "Source" form shall mean the preferred form for making modifications, 54 | including but not limited to software source code, documentation 55 | source, and configuration files. 56 | 57 | 58 | "Object" form shall mean any form resulting from mechanical 59 | transformation or translation of a Source form, including but 60 | not limited to compiled object code, generated documentation, 61 | and conversions to other media types. 62 | 63 | 64 | "Work" shall mean the work of authorship, whether in Source or 65 | Object form, made available under the License, as indicated by a 66 | copyright notice that is included in or attached to the work 67 | (an example is provided in the Appendix below). 68 | 69 | 70 | "Derivative Works" shall mean any work, whether in Source or Object 71 | form, that is based on (or derived from) the Work and for which the 72 | editorial revisions, annotations, elaborations, or other modifications 73 | represent, as a whole, an original work of authorship. For the purposes 74 | of this License, Derivative Works shall not include works that remain 75 | separable from, or merely link (or bind by name) to the interfaces of, 76 | the Work and Derivative Works thereof. 77 | 78 | 79 | "Contribution" shall mean any work of authorship, including 80 | the original version of the Work and any modifications or additions 81 | to that Work or Derivative Works thereof, that is intentionally 82 | submitted to Licensor for inclusion in the Work by the copyright owner 83 | or by an individual or Legal Entity authorized to submit on behalf of 84 | the copyright owner. For the purposes of this definition, "submitted" 85 | means any form of electronic, verbal, or written communication sent 86 | to the Licensor or its representatives, including but not limited to 87 | communication on electronic mailing lists, source code control systems, 88 | and issue tracking systems that are managed by, or on behalf of, the 89 | Licensor for the purpose of discussing and improving the Work, but 90 | excluding communication that is conspicuously marked or otherwise 91 | designated in writing by the copyright owner as "Not a Contribution." 92 | 93 | 94 | "Contributor" shall mean Licensor and any individual or Legal Entity 95 | on behalf of whom a Contribution has been received by Licensor and 96 | subsequently incorporated within the Work. 97 | 98 | 99 | 2. Grant of Copyright License. Subject to the terms and conditions of 100 | this License, each Contributor hereby grants to You a perpetual, 101 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 102 | copyright license to reproduce, prepare Derivative Works of, 103 | publicly display, publicly perform, sublicense, and distribute the 104 | Work and such Derivative Works in Source or Object form. 105 | 106 | 107 | 3. Grant of Patent License. Subject to the terms and conditions of 108 | this License, each Contributor hereby grants to You a perpetual, 109 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 110 | (except as stated in this section) patent license to make, have made, 111 | use, offer to sell, sell, import, and otherwise transfer the Work, 112 | where such license applies only to those patent claims licensable 113 | by such Contributor that are necessarily infringed by their 114 | Contribution(s) alone or by combination of their Contribution(s) 115 | with the Work to which such Contribution(s) was submitted. If You 116 | institute patent litigation against any entity (including a 117 | cross-claim or counterclaim in a lawsuit) alleging that the Work 118 | or a Contribution incorporated within the Work constitutes direct 119 | or contributory patent infringement, then any patent licenses 120 | granted to You under this License for that Work shall terminate 121 | as of the date such litigation is filed. 122 | 123 | 124 | 4. Redistribution. You may reproduce and distribute copies of the 125 | Work or Derivative Works thereof in any medium, with or without 126 | modifications, and in Source or Object form, provided that You 127 | meet the following conditions: 128 | 129 | 130 | (a) You must give any other recipients of the Work or 131 | Derivative Works a copy of this License; and 132 | 133 | 134 | (b) You must cause any modified files to carry prominent notices 135 | stating that You changed the files; and 136 | 137 | 138 | (c) You must retain, in the Source form of any Derivative Works 139 | that You distribute, all copyright, patent, trademark, and 140 | attribution notices from the Source form of the Work, 141 | excluding those notices that do not pertain to any part of 142 | the Derivative Works; and 143 | 144 | 145 | (d) If the Work includes a "NOTICE" text file as part of its 146 | distribution, then any Derivative Works that You distribute must 147 | include a readable copy of the attribution notices contained 148 | within such NOTICE file, excluding those notices that do not 149 | pertain to any part of the Derivative Works, in at least one 150 | of the following places: within a NOTICE text file distributed 151 | as part of the Derivative Works; within the Source form or 152 | documentation, if provided along with the Derivative Works; or, 153 | within a display generated by the Derivative Works, if and 154 | wherever such third-party notices normally appear. The contents 155 | of the NOTICE file are for informational purposes only and 156 | do not modify the License. You may add Your own attribution 157 | notices within Derivative Works that You distribute, alongside 158 | or as an addendum to the NOTICE text from the Work, provided 159 | that such additional attribution notices cannot be construed 160 | as modifying the License. 161 | 162 | 163 | You may add Your own copyright statement to Your modifications and 164 | may provide additional or different license terms and conditions 165 | for use, reproduction, or distribution of Your modifications, or 166 | for any such Derivative Works as a whole, provided Your use, 167 | reproduction, and distribution of the Work otherwise complies with 168 | the conditions stated in this License. 169 | 170 | 171 | 5. Submission of Contributions. Unless You explicitly state otherwise, 172 | any Contribution intentionally submitted for inclusion in the Work 173 | by You to the Licensor shall be under the terms and conditions of 174 | this License, without any additional terms or conditions. 175 | Notwithstanding the above, nothing herein shall supersede or modify 176 | the terms of any separate license agreement you may have executed 177 | with Licensor regarding such Contributions. 178 | 179 | 180 | 6. Trademarks. This License does not grant permission to use the trade 181 | names, trademarks, service marks, or product names of the Licensor, 182 | except as required for reasonable and customary use in describing the 183 | origin of the Work and reproducing the content of the NOTICE file. 184 | 185 | 186 | 7. Disclaimer of Warranty. Unless required by applicable law or 187 | agreed to in writing, Licensor provides the Work (and each 188 | Contributor provides its Contributions) on an "AS IS" BASIS, 189 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 190 | implied, including, without limitation, any warranties or conditions 191 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 192 | PARTICULAR PURPOSE. You are solely responsible for determining the 193 | appropriateness of using or redistributing the Work and assume any 194 | risks associated with Your exercise of permissions under this License. 195 | 196 | 197 | 8. Limitation of Liability. In no event and under no legal theory, 198 | whether in tort (including negligence), contract, or otherwise, 199 | unless required by applicable law (such as deliberate and grossly 200 | negligent acts) or agreed to in writing, shall any Contributor be 201 | liable to You for damages, including any direct, indirect, special, 202 | incidental, or consequential damages of any character arising as a 203 | result of this License or out of the use or inability to use the 204 | Work (including but not limited to damages for loss of goodwill, 205 | work stoppage, computer failure or malfunction, or any and all 206 | other commercial damages or losses), even if such Contributor 207 | has been advised of the possibility of such damages. 208 | 209 | 210 | 9. Accepting Warranty or Additional Liability. While redistributing 211 | the Work or Derivative Works thereof, You may choose to offer, 212 | and charge a fee for, acceptance of support, warranty, indemnity, 213 | or other liability obligations and/or rights consistent with this 214 | License. However, in accepting such obligations, You may act only 215 | on Your own behalf and on Your sole responsibility, not on behalf 216 | of any other Contributor, and only if You agree to indemnify, 217 | defend, and hold each Contributor harmless for any liability 218 | incurred by, or claims asserted against, such Contributor by reason 219 | of your accepting any such warranty or additional liability. 220 | 221 | 222 | END OF TERMS AND CONDITIONS 223 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Async Dispatcher 2 | 3 | [![crates.io version](https://img.shields.io/crates/v/async-dispatcher.svg?style=flat-square)](https://crates.io/crates/async-dispatcher) 4 | [![docs](https://docs.rs/async-dispatcher/badge.svg)](https://docs.rs/async-dispatcher) 5 | ![CI](https://github.com/zed-industries/async-dispatcher/actions/workflows/ci.yml/badge.svg) 6 | 7 | This crate allows async libraries to spawn tasks and set timers without being tied to a particular async runtime. 8 | 9 | The core of this need comes from wanting to be able to use the native OS scheduler, as written about in [Zed Decoded: Async Rust](https://zed.dev/blog/zed-decoded-async-rust). 10 | 11 | Libraries can `spawn` in a generic way: 12 | 13 | ```rust 14 | use async_dispatcher::{spawn, sleep}; 15 | 16 | pub async my_library_function() { 17 | let task = spawn(async { 18 | sleep(Duration::from_secs(1)).await; 19 | println!("in a spawned task!"); 20 | }); 21 | 22 | // ... 23 | } 24 | ``` 25 | 26 | Applications using those libraries can control how that work is dispatched by implementing the `Dispatcher` trait: 27 | 28 | ```rust 29 | use async_dispatcher::{set_dispatcher, Dispatcher, Runnable}; 30 | 31 | struct MyAppDispatcher; 32 | 33 | impl Dispatcher for MyAppDispatcher { 34 | fn dispatch(&self, runnable: Runnable) { 35 | // ... 36 | } 37 | 38 | fn dispatch_after(&self, duration: Duration, runnable: Runnable) { 39 | // ... 40 | } 41 | } 42 | 43 | fn main() { 44 | set_dispatcher(MyAppDispatcher); 45 | 46 | async_dispatcher::block_on(async move { 47 | my_library_function().await; 48 | }); 49 | } 50 | ``` 51 | -------------------------------------------------------------------------------- /macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "async-dispatcher-macros" 3 | version = "0.1.0" 4 | edition = "2018" 5 | license = "Apache-2.0" 6 | description = "Convenience macros for async-dispatcher" 7 | 8 | [lib] 9 | proc-macro = true 10 | 11 | [dependencies] 12 | quote = "1.0" 13 | syn = { version = "2.0", features = ["full"] } 14 | proc-macro2 = "1.0" 15 | -------------------------------------------------------------------------------- /macros/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2024 Zed Industries, Inc. 2 | 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | 18 | 19 | 20 | 21 | Apache License 22 | Version 2.0, January 2004 23 | http://www.apache.org/licenses/ 24 | 25 | 26 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 27 | 28 | 29 | 1. Definitions. 30 | 31 | 32 | "License" shall mean the terms and conditions for use, reproduction, 33 | and distribution as defined by Sections 1 through 9 of this document. 34 | 35 | 36 | "Licensor" shall mean the copyright owner or entity authorized by 37 | the copyright owner that is granting the License. 38 | 39 | 40 | "Legal Entity" shall mean the union of the acting entity and all 41 | other entities that control, are controlled by, or are under common 42 | control with that entity. For the purposes of this definition, 43 | "control" means (i) the power, direct or indirect, to cause the 44 | direction or management of such entity, whether by contract or 45 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 46 | outstanding shares, or (iii) beneficial ownership of such entity. 47 | 48 | 49 | "You" (or "Your") shall mean an individual or Legal Entity 50 | exercising permissions granted by this License. 51 | 52 | 53 | "Source" form shall mean the preferred form for making modifications, 54 | including but not limited to software source code, documentation 55 | source, and configuration files. 56 | 57 | 58 | "Object" form shall mean any form resulting from mechanical 59 | transformation or translation of a Source form, including but 60 | not limited to compiled object code, generated documentation, 61 | and conversions to other media types. 62 | 63 | 64 | "Work" shall mean the work of authorship, whether in Source or 65 | Object form, made available under the License, as indicated by a 66 | copyright notice that is included in or attached to the work 67 | (an example is provided in the Appendix below). 68 | 69 | 70 | "Derivative Works" shall mean any work, whether in Source or Object 71 | form, that is based on (or derived from) the Work and for which the 72 | editorial revisions, annotations, elaborations, or other modifications 73 | represent, as a whole, an original work of authorship. For the purposes 74 | of this License, Derivative Works shall not include works that remain 75 | separable from, or merely link (or bind by name) to the interfaces of, 76 | the Work and Derivative Works thereof. 77 | 78 | 79 | "Contribution" shall mean any work of authorship, including 80 | the original version of the Work and any modifications or additions 81 | to that Work or Derivative Works thereof, that is intentionally 82 | submitted to Licensor for inclusion in the Work by the copyright owner 83 | or by an individual or Legal Entity authorized to submit on behalf of 84 | the copyright owner. For the purposes of this definition, "submitted" 85 | means any form of electronic, verbal, or written communication sent 86 | to the Licensor or its representatives, including but not limited to 87 | communication on electronic mailing lists, source code control systems, 88 | and issue tracking systems that are managed by, or on behalf of, the 89 | Licensor for the purpose of discussing and improving the Work, but 90 | excluding communication that is conspicuously marked or otherwise 91 | designated in writing by the copyright owner as "Not a Contribution." 92 | 93 | 94 | "Contributor" shall mean Licensor and any individual or Legal Entity 95 | on behalf of whom a Contribution has been received by Licensor and 96 | subsequently incorporated within the Work. 97 | 98 | 99 | 2. Grant of Copyright License. Subject to the terms and conditions of 100 | this License, each Contributor hereby grants to You a perpetual, 101 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 102 | copyright license to reproduce, prepare Derivative Works of, 103 | publicly display, publicly perform, sublicense, and distribute the 104 | Work and such Derivative Works in Source or Object form. 105 | 106 | 107 | 3. Grant of Patent License. Subject to the terms and conditions of 108 | this License, each Contributor hereby grants to You a perpetual, 109 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 110 | (except as stated in this section) patent license to make, have made, 111 | use, offer to sell, sell, import, and otherwise transfer the Work, 112 | where such license applies only to those patent claims licensable 113 | by such Contributor that are necessarily infringed by their 114 | Contribution(s) alone or by combination of their Contribution(s) 115 | with the Work to which such Contribution(s) was submitted. If You 116 | institute patent litigation against any entity (including a 117 | cross-claim or counterclaim in a lawsuit) alleging that the Work 118 | or a Contribution incorporated within the Work constitutes direct 119 | or contributory patent infringement, then any patent licenses 120 | granted to You under this License for that Work shall terminate 121 | as of the date such litigation is filed. 122 | 123 | 124 | 4. Redistribution. You may reproduce and distribute copies of the 125 | Work or Derivative Works thereof in any medium, with or without 126 | modifications, and in Source or Object form, provided that You 127 | meet the following conditions: 128 | 129 | 130 | (a) You must give any other recipients of the Work or 131 | Derivative Works a copy of this License; and 132 | 133 | 134 | (b) You must cause any modified files to carry prominent notices 135 | stating that You changed the files; and 136 | 137 | 138 | (c) You must retain, in the Source form of any Derivative Works 139 | that You distribute, all copyright, patent, trademark, and 140 | attribution notices from the Source form of the Work, 141 | excluding those notices that do not pertain to any part of 142 | the Derivative Works; and 143 | 144 | 145 | (d) If the Work includes a "NOTICE" text file as part of its 146 | distribution, then any Derivative Works that You distribute must 147 | include a readable copy of the attribution notices contained 148 | within such NOTICE file, excluding those notices that do not 149 | pertain to any part of the Derivative Works, in at least one 150 | of the following places: within a NOTICE text file distributed 151 | as part of the Derivative Works; within the Source form or 152 | documentation, if provided along with the Derivative Works; or, 153 | within a display generated by the Derivative Works, if and 154 | wherever such third-party notices normally appear. The contents 155 | of the NOTICE file are for informational purposes only and 156 | do not modify the License. You may add Your own attribution 157 | notices within Derivative Works that You distribute, alongside 158 | or as an addendum to the NOTICE text from the Work, provided 159 | that such additional attribution notices cannot be construed 160 | as modifying the License. 161 | 162 | 163 | You may add Your own copyright statement to Your modifications and 164 | may provide additional or different license terms and conditions 165 | for use, reproduction, or distribution of Your modifications, or 166 | for any such Derivative Works as a whole, provided Your use, 167 | reproduction, and distribution of the Work otherwise complies with 168 | the conditions stated in this License. 169 | 170 | 171 | 5. Submission of Contributions. Unless You explicitly state otherwise, 172 | any Contribution intentionally submitted for inclusion in the Work 173 | by You to the Licensor shall be under the terms and conditions of 174 | this License, without any additional terms or conditions. 175 | Notwithstanding the above, nothing herein shall supersede or modify 176 | the terms of any separate license agreement you may have executed 177 | with Licensor regarding such Contributions. 178 | 179 | 180 | 6. Trademarks. This License does not grant permission to use the trade 181 | names, trademarks, service marks, or product names of the Licensor, 182 | except as required for reasonable and customary use in describing the 183 | origin of the Work and reproducing the content of the NOTICE file. 184 | 185 | 186 | 7. Disclaimer of Warranty. Unless required by applicable law or 187 | agreed to in writing, Licensor provides the Work (and each 188 | Contributor provides its Contributions) on an "AS IS" BASIS, 189 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 190 | implied, including, without limitation, any warranties or conditions 191 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 192 | PARTICULAR PURPOSE. You are solely responsible for determining the 193 | appropriateness of using or redistributing the Work and assume any 194 | risks associated with Your exercise of permissions under this License. 195 | 196 | 197 | 8. Limitation of Liability. In no event and under no legal theory, 198 | whether in tort (including negligence), contract, or otherwise, 199 | unless required by applicable law (such as deliberate and grossly 200 | negligent acts) or agreed to in writing, shall any Contributor be 201 | liable to You for damages, including any direct, indirect, special, 202 | incidental, or consequential damages of any character arising as a 203 | result of this License or out of the use or inability to use the 204 | Work (including but not limited to damages for loss of goodwill, 205 | work stoppage, computer failure or malfunction, or any and all 206 | other commercial damages or losses), even if such Contributor 207 | has been advised of the possibility of such damages. 208 | 209 | 210 | 9. Accepting Warranty or Additional Liability. While redistributing 211 | the Work or Derivative Works thereof, You may choose to offer, 212 | and charge a fee for, acceptance of support, warranty, indemnity, 213 | or other liability obligations and/or rights consistent with this 214 | License. However, in accepting such obligations, You may act only 215 | on Your own behalf and on Your sole responsibility, not on behalf 216 | of any other Contributor, and only if You agree to indemnify, 217 | defend, and hold each Contributor harmless for any liability 218 | incurred by, or claims asserted against, such Contributor by reason 219 | of your accepting any such warranty or additional liability. 220 | 221 | 222 | END OF TERMS AND CONDITIONS 223 | -------------------------------------------------------------------------------- /macros/src/lib.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::TokenStream; 2 | use syn::spanned::Spanned as _; 3 | 4 | #[proc_macro_attribute] 5 | pub fn test(_attr: TokenStream, item: TokenStream) -> TokenStream { 6 | let input = syn::parse_macro_input!(item as syn::ItemFn); 7 | 8 | let ret = &input.sig.output; 9 | let name = &input.sig.ident; 10 | let body = &input.block; 11 | let attrs = &input.attrs; 12 | let vis = &input.vis; 13 | 14 | if input.sig.asyncness.is_none() { 15 | return TokenStream::from(quote::quote_spanned! { input.span() => 16 | compile_error!("the async keyword is missing from the function declaration"), 17 | }); 18 | } 19 | 20 | let result = quote::quote! { 21 | #[::core::prelude::v1::test] 22 | #(#attrs)* 23 | #vis fn #name() #ret { 24 | ::async_dispatcher::set_dispatcher(::async_dispatcher::thread_dispatcher()); 25 | ::async_dispatcher::block_on(async { #body }) 26 | } 27 | }; 28 | 29 | result.into() 30 | } 31 | 32 | #[proc_macro_attribute] 33 | pub fn main(_attr: TokenStream, item: TokenStream) -> TokenStream { 34 | let input = syn::parse_macro_input!(item as syn::ItemFn); 35 | 36 | let ret = &input.sig.output; 37 | let body = &input.block; 38 | let attrs = &input.attrs; 39 | 40 | if input.sig.asyncness.is_none() { 41 | return TokenStream::from(quote::quote_spanned! { input.span() => 42 | compile_error!("the async keyword is missing from the function declaration"), 43 | }); 44 | } 45 | 46 | let result = quote::quote! { 47 | #(#attrs)* 48 | fn main() #ret { 49 | ::async_dispatcher::set_dispatcher(::async_dispatcher::thread_dispatcher()); 50 | ::async_dispatcher::block_on(async { #body }) 51 | } 52 | }; 53 | 54 | result.into() 55 | } 56 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | pub use async_task::Runnable; 2 | use futures_lite::FutureExt; 3 | use std::{ 4 | error::Error, 5 | fmt, 6 | future::Future, 7 | sync::{mpsc::RecvTimeoutError, OnceLock}, 8 | task::Poll, 9 | time::{Duration, Instant}, 10 | }; 11 | 12 | pub fn block_on(future: impl Future) -> T { 13 | futures_lite::future::block_on(future) 14 | } 15 | 16 | static DISPATCHER: OnceLock> = OnceLock::new(); 17 | 18 | pub trait Dispatcher: 'static + Send + Sync { 19 | fn dispatch(&self, runnable: Runnable); 20 | fn dispatch_after(&self, duration: Duration, runnable: Runnable); 21 | } 22 | 23 | pub fn set_dispatcher(dispatcher: impl Dispatcher) { 24 | DISPATCHER.set(Box::new(dispatcher)).ok(); 25 | } 26 | 27 | fn get_dispatcher() -> &'static dyn Dispatcher { 28 | DISPATCHER 29 | .get() 30 | .expect("The dispatcher requires a call to set_dispatcher()") 31 | .as_ref() 32 | } 33 | 34 | #[derive(Debug)] 35 | pub struct JoinHandle { 36 | task: Option>, 37 | } 38 | 39 | pub fn spawn(future: F) -> JoinHandle 40 | where 41 | F: Future + 'static + Send, 42 | F::Output: 'static + Send, 43 | { 44 | let dispatcher = get_dispatcher(); 45 | let (runnable, task) = async_task::spawn(future, |runnable| dispatcher.dispatch(runnable)); 46 | runnable.schedule(); 47 | JoinHandle { task: Some(task) } 48 | } 49 | 50 | impl Future for JoinHandle { 51 | type Output = T; 52 | 53 | fn poll( 54 | mut self: std::pin::Pin<&mut Self>, 55 | cx: &mut std::task::Context<'_>, 56 | ) -> Poll { 57 | std::pin::Pin::new( 58 | self.task 59 | .as_mut() 60 | .expect("poll should not be called after drop"), 61 | ) 62 | .poll(cx) 63 | } 64 | } 65 | 66 | impl Drop for JoinHandle { 67 | fn drop(&mut self) { 68 | self.task 69 | .take() 70 | .expect("This is the only place the option is mutated") 71 | .detach(); 72 | } 73 | } 74 | 75 | pub struct Sleep { 76 | task: async_task::Task<()>, 77 | } 78 | 79 | pub fn sleep(time: Duration) -> Sleep { 80 | let dispatcher = get_dispatcher(); 81 | let (runnable, task) = async_task::spawn(async {}, move |runnable| { 82 | dispatcher.dispatch_after(time, runnable) 83 | }); 84 | runnable.schedule(); 85 | 86 | Sleep { task } 87 | } 88 | 89 | impl Sleep { 90 | pub fn reset(&mut self, deadline: Instant) { 91 | let duration = deadline.saturating_duration_since(Instant::now()); 92 | self.task = sleep(duration).task 93 | } 94 | } 95 | 96 | impl Future for Sleep { 97 | type Output = (); 98 | 99 | fn poll( 100 | mut self: std::pin::Pin<&mut Self>, 101 | cx: &mut std::task::Context<'_>, 102 | ) -> Poll { 103 | std::pin::Pin::new(&mut self.task).poll(cx) 104 | } 105 | } 106 | 107 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] 108 | pub struct TimeoutError; 109 | 110 | impl Error for TimeoutError {} 111 | 112 | pub fn timeout( 113 | duration: Duration, 114 | future: T, 115 | ) -> impl Future> 116 | where 117 | T: Future, 118 | { 119 | let future = async move { Ok(future.await) }; 120 | let timeout = async move { 121 | sleep(duration).await; 122 | Err(TimeoutError) 123 | }; 124 | future.or(timeout) 125 | } 126 | 127 | impl fmt::Display for TimeoutError { 128 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 129 | "future has timed out".fmt(f) 130 | } 131 | } 132 | 133 | pub fn thread_dispatcher() -> impl Dispatcher { 134 | struct SimpleDispatcher { 135 | tx: std::sync::mpsc::Sender<(Runnable, Option)>, 136 | _thread: std::thread::JoinHandle<()>, 137 | } 138 | 139 | impl Dispatcher for SimpleDispatcher { 140 | fn dispatch(&self, runnable: Runnable) { 141 | self.tx.send((runnable, None)).ok(); 142 | } 143 | 144 | fn dispatch_after(&self, duration: Duration, runnable: Runnable) { 145 | self.tx 146 | .send((runnable, Some(Instant::now() + duration))) 147 | .ok(); 148 | } 149 | } 150 | 151 | let (tx, rx) = std::sync::mpsc::channel::<(Runnable, Option)>(); 152 | let _thread = std::thread::spawn(move || { 153 | let mut timers = Vec::<(Runnable, Instant)>::new(); 154 | let mut recv_timeout = Duration::MAX; 155 | loop { 156 | match rx.recv_timeout(recv_timeout) { 157 | Ok((runnable, time)) => { 158 | if let Some(time) = time { 159 | let now = Instant::now(); 160 | if time > now { 161 | let ix = match timers.binary_search_by_key(&time, |t| t.1) { 162 | Ok(i) | Err(i) => i, 163 | }; 164 | timers.insert(ix, (runnable, time)); 165 | recv_timeout = timers.first().unwrap().1 - now; 166 | continue; 167 | } 168 | } 169 | runnable.run(); 170 | } 171 | Err(RecvTimeoutError::Timeout) => { 172 | let now = Instant::now(); 173 | while let Some((_, time)) = timers.first() { 174 | if *time > now { 175 | recv_timeout = *time - now; 176 | break; 177 | } 178 | timers.remove(0).0.run(); 179 | } 180 | } 181 | Err(RecvTimeoutError::Disconnected) => break, 182 | } 183 | } 184 | }); 185 | 186 | SimpleDispatcher { tx, _thread } 187 | } 188 | 189 | #[cfg(feature = "macros")] 190 | pub use async_dispatcher_macros::test; 191 | 192 | #[cfg(feature = "macros")] 193 | pub use async_dispatcher_macros::main; 194 | --------------------------------------------------------------------------------