├── .github └── workflows │ └── test.yml ├── .gitignore ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── remote-trait-object-macro ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md └── src │ ├── helper.rs │ ├── lib.rs │ ├── service.rs │ └── service │ ├── dispatcher.rs │ ├── from_skeleton.rs │ ├── id.rs │ └── proxy.rs ├── remote-trait-object-tests ├── Cargo.toml ├── benches │ └── bench1.rs └── src │ ├── lib.rs │ ├── ping.rs │ ├── simple.rs │ ├── test_store.rs │ ├── test_store │ ├── man.rs │ ├── store.rs │ └── types.rs │ └── transport.rs └── remote-trait-object ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── flow.png └── src ├── context.rs ├── forwarder.rs ├── lib.rs ├── packet.rs ├── port.rs ├── port ├── client.rs ├── server.rs └── types.rs ├── queue.rs ├── service.rs ├── service ├── export_import.rs ├── handle.rs ├── id.rs ├── null.rs └── serde_support.rs ├── tests.rs ├── tests └── complex_trait.rs ├── transport.rs └── transport └── multiplex.rs /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | on: [push, pull_request] 2 | 3 | name: test 4 | 5 | jobs: 6 | clippy: 7 | name: Actions - clippy 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v1 11 | with: 12 | fetch-depth: 1 13 | - uses: actions-rs/toolchain@v1 14 | with: 15 | toolchain: stable 16 | components: clippy 17 | profile: minimal 18 | override: true 19 | - run: cargo fetch --verbose 20 | - run: cargo clippy --all --all-targets -- -D warnings 21 | 22 | rustfmt: 23 | name: Actions - rustfmt 24 | runs-on: ubuntu-latest 25 | steps: 26 | - uses: actions/checkout@v1 27 | with: 28 | fetch-depth: 1 29 | - uses: actions-rs/toolchain@v1 30 | with: 31 | toolchain: stable 32 | components: rustfmt 33 | profile: minimal 34 | override: true 35 | - run: cargo fmt -- --check 36 | 37 | unit-test: 38 | name: Actions - unit test 39 | runs-on: ${{ matrix.os }} 40 | strategy: 41 | matrix: 42 | os: [macOS-latest, ubuntu-latest] 43 | steps: 44 | - uses: actions/checkout@v1 45 | with: 46 | fetch-depth: 1 47 | - uses: actions-rs/toolchain@v1 48 | with: 49 | toolchain: stable 50 | profile: minimal 51 | - run: cargo fetch --verbose 52 | - run: cargo build 53 | - run: cargo test --verbose --all 54 | env: 55 | RUST_BACKTRACE: 1 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Cargo lock in subs 2 | **/Cargo.lock 3 | 4 | # Generated by Cargo 5 | **/target/ 6 | 7 | **/*.rs.bk 8 | **/*.iml 9 | .idea/ 10 | .vscode/ 11 | 12 | # macOS 13 | .DS_store 14 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "remote-trait-object", 4 | "remote-trait-object-tests", 5 | "remote-trait-object-macro", 6 | ] 7 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any person obtaining a copy 2 | of this software and associated documentation files (the "Software"), to deal 3 | in the Software without restriction, including without limitation the rights 4 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 5 | copies of the Software, and to permit persons to whom the Software is 6 | furnished to do so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in all 9 | copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 12 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 13 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 14 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 15 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 16 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 17 | SOFTWARE. 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # remote-trait-object 2 | 3 | ![Build Status](https://github.com/CodeChain-io/intertrait/workflows/ci/badge.svg) 4 | [![License](https://img.shields.io/badge/license-MIT%20OR%20Apache--2.0-blue.svg)]( 5 | https://github.com/CodeChain-io/remote-trait-object#license) 6 | [![Cargo](https://img.shields.io/crates/v/remote-trait-object.svg)]( 7 | https://crates.io/crates/remote-trait-object) 8 | [![Documentation](https://docs.rs/remote-trait-object/badge.svg)]( 9 | https://docs.rs/remote-trait-object) 10 | [![chat](https://img.shields.io/discord/569610676205781012.svg?logo=discord)](https://discord.gg/xhpdXm7) 11 | 12 | A simple and powerful Rust remote method invocation library based on Rust trait objects. 13 | 14 | See the [documentation](https://docs.rs/remote-trait-object). 15 | 16 | ## License 17 | 18 | Licensed under either of 19 | 20 | * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) 21 | 22 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 23 | 24 | at your option. 25 | 26 | ## Contribution 27 | 28 | Unless you explicitly state otherwise, any contribution intentionally submitted 29 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 30 | dual licensed as above, without any additional terms or conditions. 31 | -------------------------------------------------------------------------------- /remote-trait-object-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "remote-trait-object-macro" 3 | version = "0.4.1" 4 | authors = ["CodeChain Team "] 5 | edition = "2018" 6 | license = "MIT OR Apache-2.0" 7 | description = "Macro that expands a Rust trait to a remote-trait-object service" 8 | repository = "https://github.com/CodeChain-io/remote-trait-object" 9 | keywords = ["remote-trait-object"] 10 | include = ["src/**/*.rs", "README.md", "LICENSE-APACHE", "LICENSE-MIT"] 11 | 12 | [dependencies] 13 | proc-macro-crate = "0.1.4" 14 | proc-macro2 = "1.0" 15 | syn = { version = "1.0.16", features = ["full", "extra-traits", "visit", "fold"] } 16 | quote = "1.0" 17 | 18 | [dev-dependencies] 19 | trybuild = "1.0.23" 20 | 21 | [lib] 22 | proc-macro = true -------------------------------------------------------------------------------- /remote-trait-object-macro/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | -------------------------------------------------------------------------------- /remote-trait-object-macro/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any person obtaining a copy 2 | of this software and associated documentation files (the "Software"), to deal 3 | in the Software without restriction, including without limitation the rights 4 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 5 | copies of the Software, and to permit persons to whom the Software is 6 | furnished to do so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in all 9 | copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 12 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 13 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 14 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 15 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 16 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 17 | SOFTWARE. 18 | -------------------------------------------------------------------------------- /remote-trait-object-macro/README.md: -------------------------------------------------------------------------------- 1 | # remote-trait-object-macro 2 | 3 | Macro used for [remote-trait-object](https://github.com/CodeChain-io/remote-trait-object) 4 | -------------------------------------------------------------------------------- /remote-trait-object-macro/src/helper.rs: -------------------------------------------------------------------------------- 1 | pub fn path_of_single_ident(ident: syn::Ident) -> syn::Path { 2 | syn::Path { 3 | leading_colon: None, 4 | segments: { 5 | let mut punc = syn::punctuated::Punctuated::new(); 6 | punc.push(syn::PathSegment { 7 | ident, 8 | arguments: syn::PathArguments::None, 9 | }); 10 | punc 11 | }, 12 | } 13 | } 14 | 15 | /// In addition, it coverts str->String and [] -> Vec 16 | pub fn is_ref(the_type: &syn::Type) -> Result, String> { 17 | if *the_type 18 | == syn::parse2::(quote! { 19 | &str 20 | }) 21 | .unwrap() 22 | { 23 | return Ok(Some( 24 | syn::parse2::(quote! { 25 | String 26 | }) 27 | .unwrap(), 28 | )); 29 | } 30 | 31 | match the_type { 32 | syn::Type::Reference(x) => { 33 | if x.lifetime.is_some() { 34 | return Err("Lifetime exists".to_owned()); 35 | } 36 | if x.mutability.is_some() { 37 | return Err("Mutable".to_owned()); 38 | } 39 | match *x.elem { 40 | syn::Type::Slice(_) => Ok(Some( 41 | syn::parse2::(quote! { 42 | Vec<_> 43 | }) 44 | .unwrap(), 45 | )), 46 | _ => Ok(Some((*x.elem).clone())), 47 | } 48 | } 49 | _ => Ok(None), 50 | } 51 | } 52 | 53 | #[test] 54 | fn recognize_ref() { 55 | let t = syn::parse_str::("Vec").unwrap(); 56 | assert!(is_ref(&t).unwrap().is_none()); 57 | let t = syn::parse_str::("&Vec").unwrap(); 58 | let tu = syn::parse_str::("Vec").unwrap(); 59 | assert_eq!(is_ref(&t).unwrap().unwrap(), tu); 60 | let t = syn::parse_str::("&i32").unwrap(); 61 | let tu = syn::parse_str::("i32").unwrap(); 62 | assert_eq!(is_ref(&t).unwrap().unwrap(), tu); 63 | let t = syn::parse_str::("&str").unwrap(); 64 | let tu = syn::parse_str::("String").unwrap(); 65 | assert_eq!(is_ref(&t).unwrap().unwrap(), tu); 66 | let t = syn::parse_str::("&[u8]").unwrap(); 67 | let tu = syn::parse_str::("Vec<_>").unwrap(); 68 | assert_eq!(is_ref(&t).unwrap().unwrap(), tu); 69 | let t = syn::parse_str::("&mut i32").unwrap(); 70 | assert!(is_ref(&t).is_err()) 71 | } 72 | -------------------------------------------------------------------------------- /remote-trait-object-macro/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! This crate provides one core attribute procedural macro that is attached to the trait that you want to use as a service. 2 | //! See more details in `remote-trait-object` crate. 3 | 4 | #[macro_use] 5 | extern crate quote; 6 | 7 | mod helper; 8 | mod service; 9 | 10 | use proc_macro::TokenStream; 11 | use proc_macro2::TokenStream as TokenStream2; 12 | 13 | /// Those necessary components for the macro is specially exported in the remote-trait-object. 14 | /// The macro will always specify full path using this. 15 | fn create_env_path() -> syn::Path { 16 | syn::parse2(quote! {remote_trait_object::macro_env}).unwrap() 17 | } 18 | 19 | /// It generates all necessary helper `struct`s that makes the trait be able to be used as a service. 20 | /// 21 | /// It takes three arguments optionally 22 | /// - `serde_format = _` - Specify a type that implements `trait SerdeFormat`. The default is [serde_cbor](https://github.com/pyfisch/cbor) 23 | /// - `no_proxy` - If provided, the trait will be used only as a service object. 24 | /// - `no_skeleton` - If provided, the trait will be used only as a proxy object. 25 | /// 26 | /// There will be many new public `struct`s, but you don't have to know about them. 27 | #[proc_macro_attribute] 28 | pub fn service(args: TokenStream, input: TokenStream) -> TokenStream { 29 | match service::service(TokenStream2::from(args), TokenStream2::from(input)) { 30 | Ok(x) => TokenStream::from(x), 31 | Err(x) => TokenStream::from(x), 32 | } 33 | } 34 | 35 | /// This macro consumes the target trait, and will print the expanded code. Use this when you want to see the result of macro. 36 | #[proc_macro_attribute] 37 | pub fn service_debug(args: TokenStream, input: TokenStream) -> TokenStream { 38 | match service::service(TokenStream2::from(args), TokenStream2::from(input)) { 39 | Ok(x) => println!("{}", x), 40 | Err(x) => println!("{}", x), 41 | } 42 | TokenStream::new() 43 | } 44 | -------------------------------------------------------------------------------- /remote-trait-object-macro/src/service.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::TokenStream as TokenStream2; 2 | use syn::parse::{Parse, ParseStream}; 3 | use syn::punctuated::Punctuated; 4 | use syn::Token; 5 | 6 | pub mod dispatcher; 7 | pub mod from_skeleton; 8 | pub mod id; 9 | pub mod proxy; 10 | 11 | struct SingleArg { 12 | pub arg_name: syn::Ident, 13 | pub arg_value: T, 14 | } 15 | 16 | impl Parse for SingleArg { 17 | fn parse(input: ParseStream) -> syn::parse::Result { 18 | let arg_name = input.parse()?; 19 | input.parse::()?; 20 | let arg_value = input.parse()?; 21 | Ok(Self { 22 | arg_name, 23 | arg_value, 24 | }) 25 | } 26 | } 27 | 28 | #[derive(Default)] 29 | struct MacroArgsRaw { 30 | pub serde_format: Option, 31 | pub no_proxy: Option<()>, 32 | pub no_skeleton: Option<()>, 33 | } 34 | 35 | struct MacroArgs { 36 | pub serde_format: syn::Path, 37 | pub no_proxy: bool, 38 | pub no_skeleton: bool, 39 | } 40 | 41 | impl MacroArgsRaw { 42 | fn update(&mut self, ts: TokenStream2) -> syn::parse::Result<()> { 43 | if let Ok(arg) = syn::parse2::(ts.clone()) { 44 | return if arg == quote::format_ident!("no_proxy") { 45 | if self.no_proxy.replace(()).is_some() { 46 | Err(syn::parse::Error::new_spanned(ts, "Duplicated arguments")) 47 | } else { 48 | Ok(()) 49 | } 50 | } else if arg == quote::format_ident!("no_skeleton") { 51 | if self.no_skeleton.replace(()).is_some() { 52 | Err(syn::parse::Error::new_spanned(ts, "Duplicated arguments")) 53 | } else { 54 | Ok(()) 55 | } 56 | } else { 57 | Err(syn::parse::Error::new_spanned(ts, "Unsupported argument")) 58 | }; 59 | } 60 | 61 | let arg: SingleArg = syn::parse2(ts.clone())?; 62 | if arg.arg_name == quote::format_ident!("serde_format") { 63 | let value = syn::parse2(arg.arg_value)?; 64 | if self.serde_format.replace(value).is_some() { 65 | Err(syn::parse::Error::new_spanned(ts, "Duplicated arguments")) 66 | } else { 67 | Ok(()) 68 | } 69 | } else { 70 | Err(syn::parse::Error::new_spanned(ts, "Unsupported argument")) 71 | } 72 | } 73 | 74 | fn fill_default_values(self) -> MacroArgs { 75 | MacroArgs { 76 | serde_format: self.serde_format.unwrap_or_else(|| { 77 | syn::parse2(quote! {remote_trait_object::macro_env::DefaultSerdeFormat}).unwrap() 78 | }), 79 | no_proxy: self.no_proxy.map(|_| true).unwrap_or(false), 80 | no_skeleton: self.no_skeleton.map(|_| true).unwrap_or(false), 81 | } 82 | } 83 | } 84 | 85 | impl Parse for MacroArgsRaw { 86 | fn parse(input: ParseStream) -> syn::parse::Result { 87 | let mut result = MacroArgsRaw::default(); 88 | let args = Punctuated::::parse_terminated(input)?; 89 | for arg in args { 90 | result.update(quote! {#arg})?; 91 | } 92 | Ok(result) 93 | } 94 | } 95 | 96 | pub fn service(args: TokenStream2, input: TokenStream2) -> Result { 97 | let args: MacroArgsRaw = syn::parse2(args).map_err(|e| e.to_compile_error())?; 98 | let args = args.fill_default_values(); 99 | 100 | let source_trait = match syn::parse2::(input.clone()) { 101 | Ok(x) => x, 102 | Err(_) => { 103 | return Err( 104 | syn::Error::new_spanned(input, "You can use #[service] only on a trait") 105 | .to_compile_error(), 106 | ) 107 | } 108 | }; 109 | 110 | let id = id::generate_id(&source_trait, &args)?; 111 | let dispatcher = dispatcher::generate_dispatcher(&source_trait, &args)?; 112 | let proxy = proxy::generate_proxy(&source_trait, &args)?; 113 | let from_skeleton = from_skeleton::generate_from_skeleton(&source_trait, &args)?; 114 | 115 | Ok(quote! { 116 | #source_trait 117 | #id 118 | #dispatcher 119 | #proxy 120 | #from_skeleton 121 | }) 122 | } 123 | -------------------------------------------------------------------------------- /remote-trait-object-macro/src/service/dispatcher.rs: -------------------------------------------------------------------------------- 1 | use super::MacroArgs; 2 | use crate::create_env_path; 3 | use proc_macro2::{Span, TokenStream as TokenStream2}; 4 | 5 | pub(super) fn generate_dispatcher( 6 | source_trait: &syn::ItemTrait, 7 | args: &MacroArgs, 8 | ) -> Result { 9 | if args.no_skeleton { 10 | return Ok(TokenStream2::new()); 11 | } 12 | 13 | let env_path = create_env_path(); 14 | let trait_ident = source_trait.ident.clone(); 15 | let box_dispatcher_ident = quote::format_ident!("{}BoxDispatcher", trait_ident); 16 | let arc_dispatcher_ident = quote::format_ident!("{}ArcDispatcher", trait_ident); 17 | let rwlock_dispatcher_ident = quote::format_ident!("{}RwLockDispatcher", trait_ident); 18 | let serde_format = &args.serde_format; 19 | 20 | // TODO: If # of methods is larger than certain limit, 21 | // then introduce a closure list for the method dispatch, 22 | // instead of if-else clauses 23 | let mut if_else_clauses = TokenStream2::new(); 24 | let mut if_else_clauses_rwlock = TokenStream2::new(); 25 | 26 | let mut is_this_trait_mutable = false; 27 | 28 | for item in source_trait.items.iter() { 29 | let method = match item { 30 | syn::TraitItem::Method(x) => x, 31 | non_method => { 32 | return Err(syn::Error::new_spanned( 33 | non_method, 34 | "Service trait must have only methods", 35 | ) 36 | .to_compile_error()) 37 | } 38 | }; 39 | let id_ident = super::id::id_method_ident(source_trait, method); 40 | 41 | // Argument will be represented as a tuple. We deserialize the data as a tuple here 42 | let mut the_let_pattern = syn::PatTuple { 43 | attrs: Vec::new(), 44 | paren_token: syn::token::Paren(Span::call_site()), 45 | elems: syn::punctuated::Punctuated::new(), 46 | }; 47 | // They need annotation 48 | let mut type_annotation = syn::TypeTuple { 49 | paren_token: syn::token::Paren(Span::call_site()), 50 | elems: syn::punctuated::Punctuated::new(), 51 | }; 52 | // We apply the arguments on the designated method, performing an actuall call. 53 | let mut the_args: syn::punctuated::Punctuated = 54 | syn::punctuated::Punctuated::new(); 55 | 56 | let no_self = "All your method must take &self or &mut self (Object safety)"; 57 | let mut_self = match method 58 | .sig 59 | .inputs 60 | .first() 61 | .ok_or_else(|| syn::Error::new_spanned(method, no_self).to_compile_error())? 62 | { 63 | syn::FnArg::Typed(_) => { 64 | return Err(syn::Error::new_spanned(method, no_self).to_compile_error()) 65 | } 66 | syn::FnArg::Receiver(syn::Receiver { 67 | mutability: Some(_), 68 | .. 69 | }) => true, 70 | _ => false, 71 | }; 72 | is_this_trait_mutable |= mut_self; 73 | 74 | for (j, arg_source) in method.sig.inputs.iter().skip(1).enumerate() { 75 | let the_iden = quote::format_ident!("a{}", j + 1); 76 | the_let_pattern.elems.push(syn::Pat::Ident(syn::PatIdent { 77 | attrs: Vec::new(), 78 | by_ref: None, 79 | mutability: None, 80 | ident: the_iden, 81 | subpat: None, 82 | })); 83 | the_let_pattern 84 | .elems 85 | .push_punct(syn::token::Comma(Span::call_site())); 86 | 87 | let arg_type = match arg_source { 88 | syn::FnArg::Typed(syn::PatType { 89 | attrs: _, 90 | pat: _, 91 | colon_token: _, 92 | ty: t, 93 | }) => &**t, 94 | _ => panic!(), 95 | }; 96 | 97 | if let Some(unrefed_type) = crate::helper::is_ref(arg_type) 98 | .map_err(|e| syn::Error::new_spanned(arg_source, &e).to_compile_error())? 99 | { 100 | type_annotation.elems.push(unrefed_type); 101 | } else { 102 | type_annotation.elems.push(arg_type.clone()); 103 | } 104 | 105 | type_annotation 106 | .elems 107 | .push_punct(syn::token::Comma(Span::call_site())); 108 | 109 | let arg_ident = quote::format_ident!("a{}", j + 1); 110 | let the_arg = if crate::helper::is_ref(arg_type) 111 | .map_err(|e| syn::Error::new_spanned(arg_source, &e).to_compile_error())? 112 | .is_some() 113 | { 114 | quote! { 115 | &#arg_ident 116 | } 117 | } else { 118 | quote! { 119 | #arg_ident 120 | } 121 | }; 122 | the_args.push(syn::parse2(the_arg).unwrap()); 123 | } 124 | 125 | let stmt_deserialize = quote! { 126 | // TODO: Make the macro be able to take deserialization scheme 127 | let #the_let_pattern: #type_annotation = <#serde_format as #env_path::SerdeFormat>::from_slice(args).unwrap(); 128 | }; 129 | 130 | let method_name = method.sig.ident.clone(); 131 | let stmt_call = quote! { 132 | let result = self.object.#method_name(#the_args); 133 | }; 134 | let stmt_call_rwlock = if mut_self { 135 | quote! { 136 | let result = self.object.write().#method_name(#the_args); 137 | } 138 | } else { 139 | quote! { 140 | let result = self.object.read().#method_name(#the_args); 141 | } 142 | }; 143 | 144 | let the_return = quote! { 145 | return <#serde_format as #env_path::SerdeFormat>::to_vec(&result).unwrap(); 146 | }; 147 | 148 | if_else_clauses.extend(quote! { 149 | if method == #id_ident.load(#env_path::ID_ORDERING) { 150 | #stmt_deserialize 151 | #stmt_call 152 | #the_return 153 | } 154 | }); 155 | 156 | if_else_clauses_rwlock.extend(quote! { 157 | if method == #id_ident.load(#env_path::ID_ORDERING) { 158 | #stmt_deserialize 159 | #stmt_call_rwlock 160 | #the_return 161 | } 162 | }); 163 | } 164 | if_else_clauses.extend(quote! { 165 | panic!("Invalid remote-trait-object call. Fatal Error.") 166 | }); 167 | if_else_clauses_rwlock.extend(quote! { 168 | panic!("Invalid remote-trait-object call. Fatal Error.") 169 | }); 170 | 171 | let box_dispatcher = if is_this_trait_mutable { 172 | quote! { 173 | #[doc(hidden)] 174 | /// This type is generated by the remote-trait-object macro. 175 | /// It should never be used directly by you, so please ignore it. 176 | pub struct #box_dispatcher_ident { 177 | object: parking_lot::RwLock> 178 | } 179 | impl #box_dispatcher_ident { 180 | fn new(object: Box) -> Self { 181 | Self { 182 | object: parking_lot::RwLock::new(object) 183 | } 184 | } 185 | } 186 | impl #env_path::Dispatch for #box_dispatcher_ident { 187 | fn dispatch_and_call(&self, method: #env_path::MethodId, args: &[u8]) -> Vec { 188 | #if_else_clauses_rwlock 189 | } 190 | } 191 | impl #env_path::IntoSkeleton for Box { 192 | fn into_skeleton(self) -> #env_path::Skeleton { 193 | #env_path::create_skeleton(std::sync::Arc::new(#box_dispatcher_ident::new(self))) 194 | } 195 | } 196 | } 197 | } else { 198 | quote! { 199 | #[doc(hidden)] 200 | /// This type is generated by the remote-trait-object macro. 201 | /// It should never be used directly by you, so please ignore it. 202 | pub struct #box_dispatcher_ident { 203 | object: Box 204 | } 205 | impl #box_dispatcher_ident { 206 | fn new(object: Box) -> Self { 207 | Self { 208 | object 209 | } 210 | } 211 | } 212 | impl #env_path::Dispatch for #box_dispatcher_ident { 213 | fn dispatch_and_call(&self, method: #env_path::MethodId, args: &[u8]) -> Vec { 214 | #if_else_clauses 215 | } 216 | } 217 | impl #env_path::IntoSkeleton for Box { 218 | fn into_skeleton(self) -> #env_path::Skeleton { 219 | #env_path::create_skeleton(std::sync::Arc::new(#box_dispatcher_ident::new(self))) 220 | } 221 | } 222 | } 223 | }; 224 | 225 | let arc_dispatcher = if is_this_trait_mutable { 226 | quote! {} 227 | } else { 228 | quote! { 229 | #[doc(hidden)] 230 | /// This type is generated by the remote-trait-object macro. 231 | /// It should never be used directly by you, so please ignore it. 232 | pub struct #arc_dispatcher_ident { 233 | object: std::sync::Arc 234 | } 235 | impl #arc_dispatcher_ident { 236 | fn new(object: std::sync::Arc) -> Self { 237 | Self { 238 | object 239 | } 240 | } 241 | } 242 | impl #env_path::Dispatch for #arc_dispatcher_ident { 243 | fn dispatch_and_call(&self, method: #env_path::MethodId, args: &[u8]) -> Vec { 244 | #if_else_clauses 245 | } 246 | } 247 | impl #env_path::IntoSkeleton for std::sync::Arc { 248 | fn into_skeleton(self) -> #env_path::Skeleton { 249 | #env_path::create_skeleton(std::sync::Arc::new(#arc_dispatcher_ident::new(self))) 250 | } 251 | } 252 | } 253 | }; 254 | 255 | let rwlock_dispatcher = quote! { 256 | #[doc(hidden)] 257 | /// This type is generated by the remote-trait-object macro. 258 | /// It should never be used directly by you, so please ignore it. 259 | pub struct #rwlock_dispatcher_ident { 260 | object: std::sync::Arc> 261 | } 262 | impl #rwlock_dispatcher_ident { 263 | fn new(object: std::sync::Arc>) -> Self { 264 | Self { 265 | object 266 | } 267 | } 268 | } 269 | impl #env_path::Dispatch for #rwlock_dispatcher_ident { 270 | fn dispatch_and_call(&self, method: #env_path::MethodId, args: &[u8]) -> Vec { 271 | #if_else_clauses_rwlock 272 | } 273 | } 274 | impl #env_path::IntoSkeleton for std::sync::Arc> { 275 | fn into_skeleton(self) -> #env_path::Skeleton { 276 | #env_path::create_skeleton(std::sync::Arc::new(#rwlock_dispatcher_ident::new(self))) 277 | } 278 | } 279 | }; 280 | 281 | Ok(quote! { 282 | #box_dispatcher 283 | #arc_dispatcher 284 | #rwlock_dispatcher 285 | }) 286 | } 287 | -------------------------------------------------------------------------------- /remote-trait-object-macro/src/service/from_skeleton.rs: -------------------------------------------------------------------------------- 1 | use super::MacroArgs; 2 | use crate::create_env_path; 3 | use crate::helper::path_of_single_ident; 4 | use proc_macro2::{Span, TokenStream as TokenStream2}; 5 | use quote::ToTokens; 6 | 7 | pub(super) fn generate_from_skeleton( 8 | source_trait: &syn::ItemTrait, 9 | args: &MacroArgs, 10 | ) -> Result { 11 | if args.no_proxy { 12 | return Ok(TokenStream2::new()); 13 | } 14 | 15 | let env_path = create_env_path(); 16 | 17 | let trait_ident = source_trait.ident.clone(); 18 | let struct_ident = quote::format_ident!("{}FromSkeleton", trait_ident); 19 | let mut imported_struct = quote! { 20 | #[derive(Debug)] 21 | #[doc(hidden)] 22 | /// This type is generated by the remote-trait-object macro. 23 | /// It should never be used directly by you, so please ignore it. 24 | pub struct #struct_ident { 25 | skeleton: #env_path::Skeleton 26 | } 27 | }; 28 | let mut imported_struct_impl = syn::parse2::(quote! { 29 | impl #trait_ident for #struct_ident { 30 | } 31 | }) 32 | .unwrap(); 33 | let serde_format = &args.serde_format; 34 | 35 | for item in source_trait.items.iter() { 36 | let method = match item { 37 | syn::TraitItem::Method(x) => x, 38 | non_method => { 39 | return Err(syn::Error::new_spanned( 40 | non_method, 41 | "Service trait must have only methods", 42 | ) 43 | .to_compile_error()) 44 | } 45 | }; 46 | let id_ident = super::id::id_method_ident(source_trait, method); 47 | 48 | let mut the_method = syn::parse_str::("fn dummy() -> () {}").unwrap(); 49 | the_method.sig = method.sig.clone(); 50 | let mut arguments_in_tuple = syn::ExprTuple { 51 | attrs: Vec::new(), 52 | paren_token: syn::token::Paren(Span::call_site()), 53 | elems: syn::punctuated::Punctuated::new(), 54 | }; 55 | for arg in &method.sig.inputs { 56 | match arg { 57 | syn::FnArg::Receiver(_) => continue, // &self 58 | syn::FnArg::Typed(pattern) => { 59 | if let syn::Pat::Ident(the_arg) = &*pattern.pat { 60 | arguments_in_tuple 61 | .elems 62 | .push(syn::Expr::Path(syn::ExprPath { 63 | attrs: Vec::new(), 64 | qself: None, 65 | path: path_of_single_ident(the_arg.ident.clone()), 66 | })); 67 | } else { 68 | return Err(syn::Error::new_spanned( 69 | arg, 70 | "You must not use a pattern for the argument", 71 | ) 72 | .to_compile_error()); 73 | } 74 | } 75 | } 76 | } 77 | 78 | let the_call = quote! { 79 | let args = <#serde_format as #env_path::SerdeFormat>::to_vec(&#arguments_in_tuple).unwrap(); 80 | let result = #env_path::get_dispatch(&self.skeleton).dispatch_and_call(#id_ident.load(#env_path::ID_ORDERING), &args); 81 | <#serde_format as #env_path::SerdeFormat>::from_slice(&result).unwrap() 82 | }; 83 | the_method 84 | .block 85 | .stmts 86 | .push(syn::Stmt::Expr(syn::Expr::Verbatim(the_call))); 87 | imported_struct_impl 88 | .items 89 | .push(syn::ImplItem::Method(the_method)); 90 | } 91 | imported_struct.extend(imported_struct_impl.to_token_stream()); 92 | imported_struct.extend(quote! { 93 | impl #env_path::Service for #struct_ident { 94 | } 95 | impl #env_path::FromSkeleton for Box { 96 | fn from_skeleton(skeleton: #env_path::Skeleton) -> Self { 97 | Box::new(#struct_ident { 98 | skeleton 99 | }) 100 | } 101 | } 102 | impl #env_path::FromSkeleton for std::sync::Arc { 103 | fn from_skeleton(skeleton: #env_path::Skeleton) -> Self { 104 | std::sync::Arc::new(#struct_ident { 105 | skeleton 106 | }) 107 | } 108 | } 109 | impl #env_path::FromSkeleton for std::sync::Arc> { 110 | fn from_skeleton(skeleton: #env_path::Skeleton) -> Self { 111 | std::sync::Arc::new(parking_lot::RwLock::new(#struct_ident { 112 | skeleton 113 | })) 114 | } 115 | } 116 | }); 117 | Ok(imported_struct.to_token_stream()) 118 | } 119 | -------------------------------------------------------------------------------- /remote-trait-object-macro/src/service/id.rs: -------------------------------------------------------------------------------- 1 | use super::MacroArgs; 2 | use crate::create_env_path; 3 | use proc_macro2::{Span, TokenStream as TokenStream2}; 4 | use syn::Ident; 5 | 6 | pub fn id_method_ident(the_trait: &syn::ItemTrait, method: &syn::TraitItemMethod) -> Ident { 7 | quote::format_ident!("ID_METHOD_{}_{}", the_trait.ident, method.sig.ident) 8 | } 9 | 10 | fn lit_index(index: usize) -> syn::Lit { 11 | // We put a distinctive offset for the easy debug. 12 | syn::Lit::Int(syn::LitInt::new( 13 | &format!("{}", index + 70), 14 | Span::call_site(), 15 | )) 16 | } 17 | 18 | fn id_method_entry_ident(the_trait: &syn::ItemTrait, method: &syn::TraitItemMethod) -> Ident { 19 | quote::format_ident!("ID_METHOD_ENTRY_{}_{}", the_trait.ident, method.sig.ident) 20 | } 21 | 22 | fn id_method_setter_ident(the_trait: &syn::ItemTrait, method: &syn::TraitItemMethod) -> Ident { 23 | quote::format_ident!("id_method_setter_{}_{}", the_trait.ident, method.sig.ident) 24 | } 25 | 26 | pub(super) fn generate_id( 27 | source_trait: &syn::ItemTrait, 28 | _args: &MacroArgs, 29 | ) -> Result { 30 | let env_path = create_env_path(); 31 | let lit_trait_name = syn::LitStr::new(&format!("{}", source_trait.ident), Span::call_site()); 32 | let mut method_id_table = TokenStream2::new(); 33 | 34 | for (i, item) in source_trait.items.iter().enumerate() { 35 | let method = match item { 36 | syn::TraitItem::Method(x) => x, 37 | non_method => { 38 | return Err(syn::Error::new_spanned( 39 | non_method, 40 | "Service trait must have only methods", 41 | ) 42 | .to_compile_error()) 43 | } 44 | }; 45 | let lit_index = lit_index(i); 46 | let lit_method_name = syn::LitStr::new(&format!("{}", method.sig.ident), Span::call_site()); 47 | 48 | let id_ident = id_method_ident(&source_trait, method); 49 | let id_entry_ident = id_method_entry_ident(&source_trait, method); 50 | let id_setter_ident = id_method_setter_ident(&source_trait, method); 51 | let id_entry = quote! { 52 | #[allow(non_upper_case_globals)] 53 | static #id_ident: #env_path::MethodIdAtomic = #env_path::MethodIdAtomic::new(#lit_index); 54 | #[linkme::distributed_slice(#env_path::MID_REG)] 55 | #[allow(non_upper_case_globals)] 56 | static #id_entry_ident: (&'static str, &'static str, fn(id: #env_path::MethodId)) = 57 | (#lit_trait_name, #lit_method_name, #id_setter_ident); 58 | #[allow(non_snake_case)] 59 | fn #id_setter_ident(id: #env_path::MethodId) { 60 | #id_ident.store(id, #env_path::ID_ORDERING); 61 | } 62 | }; 63 | method_id_table.extend(id_entry); 64 | } 65 | Ok(method_id_table) 66 | } 67 | -------------------------------------------------------------------------------- /remote-trait-object-macro/src/service/proxy.rs: -------------------------------------------------------------------------------- 1 | use super::MacroArgs; 2 | use crate::create_env_path; 3 | use crate::helper::path_of_single_ident; 4 | use proc_macro2::{Span, TokenStream as TokenStream2}; 5 | use quote::ToTokens; 6 | 7 | pub(super) fn generate_proxy( 8 | source_trait: &syn::ItemTrait, 9 | args: &MacroArgs, 10 | ) -> Result { 11 | if args.no_proxy { 12 | return Ok(TokenStream2::new()); 13 | } 14 | 15 | let env_path = create_env_path(); 16 | 17 | let trait_ident = source_trait.ident.clone(); 18 | let struct_ident = quote::format_ident!("{}Proxy", trait_ident); 19 | let mut imported_struct = quote! { 20 | #[derive(Debug)] 21 | #[doc(hidden)] 22 | /// This type is generated by the remote-trait-object macro. 23 | /// It should never be used directly by you, so please ignore it. 24 | pub struct #struct_ident { 25 | handle: #env_path::Handle 26 | } 27 | }; 28 | let mut imported_struct_impl = syn::parse2::(quote! { 29 | impl #trait_ident for #struct_ident { 30 | } 31 | }) 32 | .unwrap(); 33 | let serde_format = &args.serde_format; 34 | 35 | for item in source_trait.items.iter() { 36 | let method = match item { 37 | syn::TraitItem::Method(x) => x, 38 | non_method => { 39 | return Err(syn::Error::new_spanned( 40 | non_method, 41 | "Service trait must have only methods", 42 | ) 43 | .to_compile_error()) 44 | } 45 | }; 46 | let id_ident = super::id::id_method_ident(source_trait, method); 47 | 48 | let mut the_method = syn::parse_str::("fn dummy() -> () {}").unwrap(); 49 | the_method.sig = method.sig.clone(); 50 | let mut arguments_in_tuple = syn::ExprTuple { 51 | attrs: Vec::new(), 52 | paren_token: syn::token::Paren(Span::call_site()), 53 | elems: syn::punctuated::Punctuated::new(), 54 | }; 55 | for arg in &method.sig.inputs { 56 | match arg { 57 | syn::FnArg::Receiver(_) => continue, // &self 58 | syn::FnArg::Typed(pattern) => { 59 | if let syn::Pat::Ident(the_arg) = &*pattern.pat { 60 | arguments_in_tuple 61 | .elems 62 | .push(syn::Expr::Path(syn::ExprPath { 63 | attrs: Vec::new(), 64 | qself: None, 65 | path: path_of_single_ident(the_arg.ident.clone()), 66 | })); 67 | } else { 68 | return Err(syn::Error::new_spanned( 69 | arg, 70 | "You must not use a pattern for the argument", 71 | ) 72 | .to_compile_error()); 73 | } 74 | } 75 | } 76 | } 77 | 78 | let the_call = quote! { 79 | self.handle.call::<#serde_format, _, _>(#id_ident.load(#env_path::ID_ORDERING), &#arguments_in_tuple) 80 | }; 81 | the_method 82 | .block 83 | .stmts 84 | .push(syn::Stmt::Expr(syn::Expr::Verbatim(the_call))); 85 | imported_struct_impl 86 | .items 87 | .push(syn::ImplItem::Method(the_method)); 88 | } 89 | imported_struct.extend(imported_struct_impl.to_token_stream()); 90 | imported_struct.extend(quote! { 91 | impl #env_path::Service for #struct_ident { 92 | } 93 | impl #env_path::ImportProxy for Box { 94 | fn import_proxy(port: std::sync::Weak, handle: #env_path::HandleToExchange) -> Self { 95 | Box::new(#struct_ident { 96 | handle: #env_path::Handle::new(handle, port), 97 | }) 98 | } 99 | } 100 | impl #env_path::ImportProxy for std::sync::Arc { 101 | fn import_proxy(port: std::sync::Weak, handle: #env_path::HandleToExchange) -> Self { 102 | std::sync::Arc::new(#struct_ident { 103 | handle: #env_path::Handle::new(handle, port), 104 | }) 105 | } 106 | } 107 | impl #env_path::ImportProxy for std::sync::Arc> { 108 | fn import_proxy(port: std::sync::Weak, handle: #env_path::HandleToExchange) -> Self { 109 | std::sync::Arc::new(parking_lot::RwLock::new(#struct_ident { 110 | handle: #env_path::Handle::new(handle, port), 111 | })) 112 | } 113 | } 114 | }); 115 | Ok(imported_struct.to_token_stream()) 116 | } 117 | -------------------------------------------------------------------------------- /remote-trait-object-tests/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "remote-trait-object-tests" 3 | version = "0.1.0" 4 | authors = ["CodeChain Team "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | crossbeam = "0.7.3" 9 | env_logger = "0.7.1" 10 | hex = "0.4.2" 11 | log = "0.4.8" 12 | once_cell = "1.3.1" 13 | remote-trait-object = { version = "0.5.0", path = "../remote-trait-object"} 14 | serde = { version = "1.0", features = ["derive"] } 15 | linkme = "0.2.3" 16 | parking_lot = "0.11.1" 17 | bincode = "1.3.1" 18 | 19 | [dev-dependencies] 20 | criterion = "0.3" 21 | 22 | [[bench]] 23 | name = "bench1" 24 | harness = false 25 | -------------------------------------------------------------------------------- /remote-trait-object-tests/benches/bench1.rs: -------------------------------------------------------------------------------- 1 | use criterion::{black_box, criterion_group, criterion_main, Criterion}; 2 | use remote_trait_object_tests::{massive_no_export, massive_with_export}; 3 | 4 | pub fn no_export(c: &mut Criterion) { 5 | c.bench_function("no_export_100", |b| { 6 | b.iter(|| massive_no_export(black_box(100))) 7 | }); 8 | } 9 | 10 | pub fn with_export(c: &mut Criterion) { 11 | c.bench_function("with_export_100", |b| { 12 | b.iter(|| massive_with_export(black_box(100))) 13 | }); 14 | } 15 | 16 | criterion_group!(benches, no_export, with_export); 17 | criterion_main!(benches); 18 | -------------------------------------------------------------------------------- /remote-trait-object-tests/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate log; 3 | 4 | #[cfg(test)] 5 | mod ping; 6 | mod simple; 7 | mod test_store; 8 | pub mod transport; 9 | 10 | pub use test_store::{massive_no_export, massive_with_export}; 11 | -------------------------------------------------------------------------------- /remote-trait-object-tests/src/ping.rs: -------------------------------------------------------------------------------- 1 | use remote_trait_object::*; 2 | use std::sync::{Arc, Barrier}; 3 | use std::thread; 4 | 5 | #[service] 6 | pub trait Hello: Service { 7 | fn hey(&self) -> ServiceRef; 8 | } 9 | 10 | struct SimpleHello { 11 | barrier: Arc, 12 | } 13 | 14 | impl Service for SimpleHello {} 15 | 16 | impl Hello for SimpleHello { 17 | fn hey(&self) -> ServiceRef { 18 | ServiceRef::create_export(Box::new(SimplePing { 19 | barrier: Arc::clone(&self.barrier), 20 | }) as Box) 21 | } 22 | } 23 | 24 | #[service] 25 | pub trait Ping: Service { 26 | fn ping(&self); 27 | fn ping_mut(&mut self); 28 | fn ping_barrier(&self); 29 | } 30 | 31 | struct SimplePing { 32 | barrier: Arc, 33 | } 34 | 35 | impl Service for SimplePing {} 36 | 37 | impl Ping for SimplePing { 38 | fn ping(&self) {} 39 | 40 | fn ping_mut(&mut self) {} 41 | 42 | fn ping_barrier(&self) { 43 | self.barrier.wait(); 44 | } 45 | } 46 | 47 | #[allow(clippy::type_complexity)] 48 | fn run( 49 | barrier: Arc, 50 | ) -> ( 51 | (Context, ServiceToImport), 52 | (Context, ServiceToImport), 53 | ) { 54 | let crate::transport::TransportEnds { 55 | recv1, 56 | send1, 57 | recv2, 58 | send2, 59 | } = crate::transport::create(); 60 | ( 61 | Context::with_initial_service( 62 | Config::default_setup(), 63 | send1, 64 | recv1, 65 | ServiceToExport::new(Box::new(SimpleHello { 66 | barrier: Arc::clone(&barrier), 67 | }) as Box), 68 | ), 69 | Context::with_initial_service( 70 | Config::default_setup(), 71 | send2, 72 | recv2, 73 | ServiceToExport::new(Box::new(SimpleHello { barrier }) as Box), 74 | ), 75 | ) 76 | } 77 | 78 | #[test] 79 | fn ping1() { 80 | let barrier = Arc::new(Barrier::new(1)); 81 | let ((_ctx1, hello1), (_ctx2, hello2)) = run(Arc::clone(&barrier)); 82 | let hello1: Box = hello1.into_proxy(); 83 | let hello2: Box = hello2.into_proxy(); 84 | 85 | let ping1: Box = hello1.hey().unwrap_import().into_proxy(); 86 | let ping2: Box = hello2.hey().unwrap_import().into_proxy(); 87 | 88 | ping1.ping(); 89 | ping2.ping(); 90 | 91 | drop(hello1); 92 | drop(hello2); 93 | } 94 | 95 | #[test] 96 | fn ping_concurrent1() { 97 | let n = 6; 98 | for _ in 0..100 { 99 | let barrier = Arc::new(Barrier::new(n + 1)); 100 | let ((_ctx1, hello1), (_ctx2, hello2)) = run(Arc::clone(&barrier)); 101 | let hello1: Box = hello1.into_proxy(); 102 | let hello2: Box = hello2.into_proxy(); 103 | 104 | let pings: Vec> = (0..n) 105 | .map(|_| hello2.hey().unwrap_import().into_proxy()) 106 | .collect(); 107 | let joins: Vec> = pings 108 | .into_iter() 109 | .map(|ping| { 110 | thread::spawn(move || { 111 | ping.ping_barrier(); 112 | }) 113 | }) 114 | .collect(); 115 | barrier.wait(); 116 | for join in joins { 117 | join.join().unwrap(); 118 | } 119 | 120 | drop(hello1); 121 | drop(hello2); 122 | } 123 | } 124 | 125 | #[test] 126 | fn ping_concurrent2() { 127 | let n = 6; 128 | for _ in 0..100 { 129 | let barrier = Arc::new(Barrier::new(n + 1)); 130 | let ((_ctx1, hello1), (_ctx2, hello2)) = run(Arc::clone(&barrier)); 131 | let hello1: Box = hello1.into_proxy(); 132 | let hello2: Box = hello2.into_proxy(); 133 | 134 | let ping: Arc = hello2.hey().unwrap_import().into_proxy(); 135 | 136 | let joins: Vec> = (0..n) 137 | .map(|_| { 138 | let ping_ = Arc::clone(&ping); 139 | thread::spawn(move || { 140 | ping_.ping_barrier(); 141 | }) 142 | }) 143 | .collect(); 144 | barrier.wait(); 145 | for join in joins { 146 | join.join().unwrap(); 147 | } 148 | 149 | drop(hello1); 150 | drop(hello2); 151 | } 152 | } 153 | 154 | #[test] 155 | #[should_panic(expected = "You invoked a method of a null proxy object.")] 156 | fn null_proxy() { 157 | let barrier = Arc::new(Barrier::new(1)); 158 | let ((ctx1, _), (_ctx2, _)) = run(Arc::clone(&barrier)); 159 | let null_handle = remote_trait_object::raw_exchange::HandleToExchange::create_null(); 160 | let null_proxy: Box = 161 | remote_trait_object::raw_exchange::import_service_from_handle(&ctx1, null_handle); 162 | null_proxy.ping(); 163 | } 164 | 165 | #[test] 166 | #[should_panic(expected = "You invoked a method of a null proxy object.")] 167 | fn null_proxy2() { 168 | let null_proxy: Box = remote_trait_object::raw_exchange::import_null_proxy(); 169 | null_proxy.ping(); 170 | } 171 | -------------------------------------------------------------------------------- /remote-trait-object-tests/src/simple.rs: -------------------------------------------------------------------------------- 1 | //! This is a working version of the example in the crate-level documentation of `remote-trait-object` 2 | 3 | use remote_trait_object::*; 4 | 5 | #[service] 6 | pub trait CreditCard: Service { 7 | fn pay(&mut self, ammount: u64) -> Result<(), ()>; 8 | } 9 | struct SomeCreditCard { 10 | money: u64, 11 | } 12 | impl Service for SomeCreditCard {} 13 | impl CreditCard for SomeCreditCard { 14 | fn pay(&mut self, ammount: u64) -> Result<(), ()> { 15 | if ammount <= self.money { 16 | self.money -= ammount; 17 | Ok(()) 18 | } else { 19 | Err(()) 20 | } 21 | } 22 | } 23 | 24 | #[service] 25 | pub trait PizzaStore: Service { 26 | fn order_pizza(&self, credit_card: ServiceRef) -> Result; 27 | } 28 | struct SomePizzaStore; 29 | impl Service for SomePizzaStore {} 30 | impl PizzaStore for SomePizzaStore { 31 | fn order_pizza(&self, credit_card: ServiceRef) -> Result { 32 | let mut credit_card_proxy: Box = credit_card.unwrap_import().into_proxy(); 33 | credit_card_proxy.pay(10)?; 34 | Ok("Tasty Pizza".to_owned()) 35 | } 36 | } 37 | 38 | #[test] 39 | fn test() { 40 | let crate::transport::TransportEnds { 41 | recv1, 42 | send1, 43 | recv2, 44 | send2, 45 | } = crate::transport::create(); 46 | 47 | let _context_pizza_town = Context::with_initial_service_export( 48 | Config::default_setup(), 49 | send1, 50 | recv1, 51 | ServiceToExport::new(Box::new(SomePizzaStore) as Box), 52 | ); 53 | 54 | let (_context_customer, pizza_store): (_, ServiceToImport) = 55 | Context::with_initial_service_import(Config::default_setup(), send2, recv2); 56 | let pizza_store_proxy: Box = pizza_store.into_proxy(); 57 | 58 | let my_credit_card = Box::new(SomeCreditCard { money: 11 }) as Box; 59 | assert_eq!( 60 | pizza_store_proxy 61 | .order_pizza(ServiceRef::create_export(my_credit_card)) 62 | .unwrap(), 63 | "Tasty Pizza" 64 | ); 65 | 66 | let my_credit_card = Box::new(SomeCreditCard { money: 9 }) as Box; 67 | assert!(pizza_store_proxy 68 | .order_pizza(ServiceRef::create_export(my_credit_card)) 69 | .is_err()); 70 | } 71 | -------------------------------------------------------------------------------- /remote-trait-object-tests/src/test_store.rs: -------------------------------------------------------------------------------- 1 | mod man; 2 | mod store; 3 | mod types; 4 | 5 | pub use man::{massive_no_export, massive_with_export}; 6 | -------------------------------------------------------------------------------- /remote-trait-object-tests/src/test_store/man.rs: -------------------------------------------------------------------------------- 1 | use super::store::run_store; 2 | use super::types::*; 3 | use remote_trait_object::*; 4 | 5 | struct MyCreditCard { 6 | balance: u32, 7 | } 8 | 9 | impl CreditCard for MyCreditCard { 10 | fn pay(&mut self, money: u32) -> Result<(), ()> { 11 | if self.balance >= money { 12 | self.balance -= money; 13 | Ok(()) 14 | } else { 15 | Err(()) 16 | } 17 | } 18 | } 19 | 20 | impl Service for MyCreditCard {} 21 | 22 | fn test_runner(f: impl Fn(Box)) { 23 | let crate::transport::TransportEnds { 24 | recv1, 25 | send1, 26 | recv2, 27 | send2, 28 | } = crate::transport::create(); 29 | let store_runner = std::thread::Builder::new() 30 | .name("Store Runner".to_owned()) 31 | .spawn(move || run_store((send2, recv2))) 32 | .unwrap(); 33 | 34 | let (rto_context, store): (Context, ServiceToImport) = 35 | Context::with_initial_service_import(Config::default_setup(), send1, recv1); 36 | let store: Box = store.into_proxy(); 37 | 38 | f(store); 39 | 40 | rto_context.disable_garbage_collection(); 41 | drop(rto_context); 42 | store_runner.join().unwrap(); 43 | } 44 | 45 | #[cfg(test)] 46 | mod tests { 47 | use super::*; 48 | use parking_lot::RwLock; 49 | use std::sync::{Arc, Barrier}; 50 | 51 | #[test] 52 | fn test_order1() { 53 | fn f(store: Box) { 54 | assert_eq!(store.order_coke("Cherry", 4), "Here's a Cherry coke"); 55 | assert_eq!(store.order_coke("Cherry", 3), "Here's a Cherry coke"); 56 | assert_eq!(store.order_coke("Cherry", 2), "Not enough money"); 57 | 58 | assert_eq!( 59 | store.order_pizza(Pizza::Pepperoni, 13), 60 | "Here's a delicious pepperoni pizza" 61 | ); 62 | assert_eq!(store.order_pizza(Pizza::Pepperoni, 12), "Not enough money"); 63 | } 64 | test_runner(f); 65 | } 66 | 67 | #[test] 68 | fn test_order2() { 69 | fn f(store: Box) { 70 | let card = Arc::new(RwLock::new(MyCreditCard { balance: 11 })); 71 | let card_to_give = card.clone() as Arc>; 72 | assert_eq!( 73 | store.order_pizza_credit_card( 74 | Pizza::Veggie, 75 | ServiceRef::create_export(card_to_give.clone()) 76 | ), 77 | "Here's a delicious veggie pizza" 78 | ); 79 | assert_eq!( 80 | store.order_pizza_credit_card( 81 | Pizza::Veggie, 82 | ServiceRef::create_export(card_to_give.clone()) 83 | ), 84 | "Not enough balance" 85 | ); 86 | card.write().balance += 10; 87 | assert_eq!( 88 | store.order_pizza_credit_card( 89 | Pizza::Veggie, 90 | ServiceRef::create_export(card_to_give) 91 | ), 92 | "Here's a delicious veggie pizza" 93 | ); 94 | } 95 | test_runner(f); 96 | } 97 | 98 | #[allow(clippy::same_item_push)] 99 | #[test] 100 | fn test_order3() { 101 | fn f(store: Box) { 102 | let n = 64; 103 | let card = Arc::new(RwLock::new(MyCreditCard { 104 | balance: 11 * n as u32, 105 | })); 106 | 107 | let start = Arc::new(Barrier::new(n)); 108 | let mut threads = Vec::new(); 109 | 110 | let store: Arc = Arc::from(store); 111 | for _ in 0..n { 112 | let store = store.clone(); 113 | let start = start.clone(); 114 | let card_to_give = card.clone() as Arc>; 115 | threads.push(std::thread::spawn(move || { 116 | start.wait(); 117 | assert_eq!( 118 | store.order_pizza_credit_card( 119 | Pizza::Pineapple, 120 | ServiceRef::create_export(card_to_give) 121 | ), 122 | "Here's a delicious pineapple pizza" 123 | ); 124 | })); 125 | } 126 | 127 | for t in threads { 128 | t.join().unwrap(); 129 | } 130 | } 131 | test_runner(f); 132 | } 133 | 134 | #[test] 135 | fn drop_service_which_holds_remote() { 136 | let crate::transport::TransportEnds { 137 | recv1, 138 | send1, 139 | recv2, 140 | send2, 141 | } = crate::transport::create(); 142 | 143 | let store_runner = std::thread::Builder::new() 144 | .name("Store Runner".to_owned()) 145 | .spawn(move || run_store((send2, recv2))) 146 | .unwrap(); 147 | 148 | let (rto_context, store): (Context, ServiceToImport) = 149 | Context::with_initial_service_import(Config::default_setup(), send1, recv1); 150 | let mut store: Box = store.into_proxy(); 151 | 152 | let card = Box::new(MyCreditCard { balance: 0 }) as Box; 153 | store.register_card(ServiceRef::create_export(card)); 154 | 155 | rto_context.disable_garbage_collection(); 156 | // This must not fail 157 | drop(store); 158 | rto_context.disable_garbage_collection(); 159 | drop(rto_context); 160 | store_runner.join().unwrap(); 161 | } 162 | 163 | #[test] 164 | fn credit_card_cast() { 165 | let crate::transport::TransportEnds { 166 | recv1, 167 | send1, 168 | recv2, 169 | send2, 170 | } = crate::transport::create(); 171 | let store_runner = std::thread::Builder::new() 172 | .name("Store Runner".to_owned()) 173 | .spawn(move || run_store((send2, recv2))) 174 | .unwrap(); 175 | 176 | let (rto_context, store): (Context, ServiceToImport) = 177 | Context::with_initial_service_import(Config::default_setup(), send1, recv1); 178 | let store: Box = store.cast_service().unwrap().into_proxy(); 179 | assert_eq!( 180 | store.order_pizza(Pizza::Pepperoni, &&&&&&&&&&&&&&13), 181 | "Here's a delicious pepperoni pizza" 182 | ); 183 | 184 | drop(store); 185 | rto_context.disable_garbage_collection(); 186 | drop(rto_context); 187 | 188 | store_runner.join().unwrap(); 189 | } 190 | 191 | #[test] 192 | fn no_connection() { 193 | let pizza_store = super::super::store::create_store(); 194 | let card = Arc::new(RwLock::new(MyCreditCard { balance: 11 })); 195 | let card_to_give = card.clone() as Arc>; 196 | assert_eq!( 197 | pizza_store.order_pizza_credit_card( 198 | Pizza::Veggie, 199 | ServiceRef::create_export(card_to_give.clone()) 200 | ), 201 | "Here's a delicious veggie pizza" 202 | ); 203 | assert_eq!( 204 | pizza_store.order_pizza_credit_card( 205 | Pizza::Veggie, 206 | ServiceRef::create_export(card_to_give.clone()) 207 | ), 208 | "Not enough balance" 209 | ); 210 | card.write().balance += 10; 211 | assert_eq!( 212 | pizza_store 213 | .order_pizza_credit_card(Pizza::Veggie, ServiceRef::create_export(card_to_give)), 214 | "Here's a delicious veggie pizza" 215 | ); 216 | } 217 | } 218 | 219 | pub fn massive_no_export(n: usize) { 220 | fn f(n: usize, store: Box) { 221 | for _ in 0..n { 222 | assert_eq!( 223 | store.order_pizza(Pizza::Pepperoni, 13), 224 | "Here's a delicious pepperoni pizza" 225 | ); 226 | } 227 | } 228 | test_runner(|store: Box| f(n, store)); 229 | } 230 | 231 | pub fn massive_with_export(n: usize) { 232 | fn f(n: usize, store: Box) { 233 | for _ in 0..n { 234 | let card = Box::new(MyCreditCard { balance: 13 }) as Box; 235 | assert_eq!( 236 | store.order_pizza_credit_card(Pizza::Pepperoni, ServiceRef::create_export(card)), 237 | "Here's a delicious pepperoni pizza" 238 | ); 239 | } 240 | } 241 | test_runner(|store: Box| f(n, store)); 242 | } 243 | -------------------------------------------------------------------------------- /remote-trait-object-tests/src/test_store/store.rs: -------------------------------------------------------------------------------- 1 | use super::types::*; 2 | use crate::transport::{IntraRecv, IntraSend}; 3 | use remote_trait_object::*; 4 | 5 | struct MyPizzaStore { 6 | vat: u32, 7 | registered_card: Option>, 8 | } 9 | 10 | impl MyPizzaStore { 11 | fn order_pizza_common(&self, menu: Pizza) -> (u32, &'static str) { 12 | match menu { 13 | Pizza::Pepperoni => (12, "pepperoni pizza"), 14 | Pizza::Veggie => (8, "veggie pizza"), 15 | Pizza::Pineapple => (10, "pineapple pizza"), 16 | } 17 | } 18 | } 19 | 20 | impl Store for MyPizzaStore { 21 | fn order_pizza(&self, menu: Pizza, money: u32) -> String { 22 | let (price, name) = self.order_pizza_common(menu); 23 | if price + self.vat <= money { 24 | format!("Here's a delicious {}", name) 25 | } else { 26 | "Not enough money".to_owned() 27 | } 28 | } 29 | 30 | fn order_coke(&self, flavor: &str, money: u32) -> String { 31 | let price = match flavor { 32 | "Plain" => 1, 33 | "Cherry" => 2, 34 | "Zero" => 3, 35 | _ => return "Not available".to_owned(), 36 | }; 37 | if price + self.vat <= money { 38 | format!("Here's a {} coke", flavor) 39 | } else { 40 | "Not enough money".to_owned() 41 | } 42 | } 43 | 44 | fn order_pizza_credit_card( 45 | &self, 46 | menu: Pizza, 47 | credit_card: ServiceRef, 48 | ) -> String { 49 | let mut credit_card: Box = credit_card.into_object(); 50 | let (price, name) = self.order_pizza_common(menu); 51 | let result = credit_card.pay(price + self.vat); 52 | match result { 53 | Ok(_) => format!("Here's a delicious {}", name), 54 | Err(_) => "Not enough balance".to_owned(), 55 | } 56 | } 57 | 58 | fn register_card(&mut self, credit_card: ServiceRef) { 59 | self.registered_card 60 | .replace(credit_card.unwrap_import().into_proxy()); 61 | } 62 | } 63 | 64 | impl Service for MyPizzaStore {} 65 | 66 | pub fn run_store(transport: (IntraSend, IntraRecv)) { 67 | let (transport_send, transport_recv) = transport; 68 | let rto_context = Context::with_initial_service_export( 69 | Config::default_setup(), 70 | transport_send, 71 | transport_recv, 72 | ServiceToExport::new(Box::new(MyPizzaStore { 73 | vat: 1, 74 | registered_card: None, 75 | }) as Box), 76 | ); 77 | rto_context.wait(None).unwrap(); 78 | } 79 | 80 | #[cfg(test)] 81 | pub fn create_store() -> Box { 82 | Box::new(MyPizzaStore { 83 | vat: 1, 84 | registered_card: None, 85 | }) 86 | } 87 | -------------------------------------------------------------------------------- /remote-trait-object-tests/src/test_store/types.rs: -------------------------------------------------------------------------------- 1 | use remote_trait_object::*; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | pub struct Bincode; 5 | 6 | impl SerdeFormat for Bincode { 7 | fn to_vec(s: &S) -> Result, ()> { 8 | bincode::serialize(s).map_err(|_| ()) 9 | } 10 | 11 | fn from_slice(data: &[u8]) -> Result { 12 | bincode::deserialize(data).map_err(|_| ()) 13 | } 14 | } 15 | 16 | #[derive(Debug, Serialize, Deserialize)] 17 | pub enum Pizza { 18 | Pepperoni, 19 | Veggie, 20 | Pineapple, 21 | } 22 | 23 | #[service] 24 | pub trait CreditCard: Service { 25 | fn pay(&mut self, money: u32) -> Result<(), ()>; 26 | } 27 | 28 | /// We use a different format for test 29 | #[service(serde_format = Bincode)] 30 | pub trait Store: Service { 31 | fn order_pizza(&self, menu: Pizza, money: u32) -> String; 32 | fn order_coke(&self, flavor: &str, money: u32) -> String; 33 | fn order_pizza_credit_card( 34 | &self, 35 | menu: Pizza, 36 | credit_card: ServiceRef, 37 | ) -> String; 38 | fn register_card(&mut self, credit_card: ServiceRef); 39 | } 40 | 41 | // Some variations of traits for tests 42 | 43 | /// This fails to compile without `no_skeleton` 44 | #[service(no_skeleton, serde_format = Bincode)] 45 | pub trait WeirdSmallStore: Service { 46 | fn order_pizza(&self, menu: Pizza, money: &&&&&&&&&&&&&&u32) -> String; 47 | } 48 | -------------------------------------------------------------------------------- /remote-trait-object-tests/src/transport.rs: -------------------------------------------------------------------------------- 1 | use crossbeam::channel::{bounded, Receiver, Select, SelectTimeoutError, Sender}; 2 | use remote_trait_object::transport::*; 3 | 4 | #[derive(Debug)] 5 | pub struct IntraSend(Sender>); 6 | 7 | impl TransportSend for IntraSend { 8 | fn send( 9 | &self, 10 | data: &[u8], 11 | timeout: Option, 12 | ) -> Result<(), TransportError> { 13 | if let Some(timeout) = timeout { 14 | // FIXME: Discern timeout error 15 | self.0 16 | .send_timeout(data.to_vec(), timeout) 17 | .map_err(|_| TransportError::Custom) 18 | } else { 19 | self.0 20 | .send(data.to_vec()) 21 | .map_err(|_| TransportError::Custom) 22 | } 23 | } 24 | 25 | fn create_terminator(&self) -> Box { 26 | unimplemented!() 27 | } 28 | } 29 | 30 | pub struct IntraRecv { 31 | data_receiver: Receiver>, 32 | terminator_receiver: Receiver<()>, 33 | terminator: Sender<()>, 34 | } 35 | 36 | pub struct Terminator(Sender<()>); 37 | 38 | impl Terminate for Terminator { 39 | fn terminate(&self) { 40 | if let Err(err) = self.0.send(()) { 41 | debug!("Terminate is called after receiver is closed {}", err); 42 | }; 43 | } 44 | } 45 | 46 | impl TransportRecv for IntraRecv { 47 | fn recv(&self, timeout: Option) -> Result, TransportError> { 48 | let mut selector = Select::new(); 49 | let data_receiver_index = selector.recv(&self.data_receiver); 50 | let terminator_index = selector.recv(&self.terminator_receiver); 51 | 52 | let selected_op = if let Some(timeout) = timeout { 53 | match selector.select_timeout(timeout) { 54 | Err(SelectTimeoutError) => return Err(TransportError::TimeOut), 55 | Ok(op) => op, 56 | } 57 | } else { 58 | selector.select() 59 | }; 60 | 61 | let data = match selected_op.index() { 62 | i if i == data_receiver_index => match selected_op.recv(&self.data_receiver) { 63 | Ok(data) => data, 64 | Err(_) => { 65 | debug!("Counterparty connection is closed in Intra"); 66 | return Err(TransportError::Custom); 67 | } 68 | }, 69 | i if i == terminator_index => { 70 | let _ = selected_op 71 | .recv(&self.terminator_receiver) 72 | .expect("Terminator should be dropped after this thread"); 73 | return Err(TransportError::Termination); 74 | } 75 | _ => unreachable!(), 76 | }; 77 | 78 | Ok(data) 79 | } 80 | 81 | fn create_terminator(&self) -> Box { 82 | Box::new(Terminator(self.terminator.clone())) 83 | } 84 | } 85 | 86 | pub struct TransportEnds { 87 | pub send1: IntraSend, 88 | pub recv1: IntraRecv, 89 | pub send2: IntraSend, 90 | pub recv2: IntraRecv, 91 | } 92 | 93 | pub fn create() -> TransportEnds { 94 | let (a_sender, a_receiver) = bounded(256); 95 | let (a_termination_sender, a_termination_receiver) = bounded(1); 96 | let (b_sender, b_receiver) = bounded(256); 97 | let (b_termination_sender, b_termination_receiver) = bounded(1); 98 | 99 | let send1 = IntraSend(b_sender); 100 | let recv1 = IntraRecv { 101 | data_receiver: a_receiver, 102 | terminator_receiver: a_termination_receiver, 103 | terminator: a_termination_sender, 104 | }; 105 | 106 | let send2 = IntraSend(a_sender); 107 | let recv2 = IntraRecv { 108 | data_receiver: b_receiver, 109 | terminator_receiver: b_termination_receiver, 110 | terminator: b_termination_sender, 111 | }; 112 | 113 | TransportEnds { 114 | recv1, 115 | send1, 116 | recv2, 117 | send2, 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /remote-trait-object/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "remote-trait-object" 3 | version = "0.5.0" 4 | authors = ["CodeChain Team "] 5 | edition = "2018" 6 | license = "MIT OR Apache-2.0" 7 | description = "A remote method invocation library based on trait objects" 8 | repository = "https://github.com/CodeChain-io/remote-trait-object" 9 | keywords = ["rmi", "protocol", "network", "server"] 10 | categories = ["network-programming"] 11 | include = ["src/**/*.rs", "README.md", "LICENSE-APACHE", "LICENSE-MIT"] 12 | 13 | [dependencies] 14 | crossbeam = "0.7.3" 15 | log = "0.4.8" 16 | parking_lot = "0.11.1" 17 | threadpool = "1.8.1" 18 | serde = { version = "1.0", features = ["derive"] } 19 | serde_cbor = "0.11.1" 20 | bincode = "1.3.1" 21 | linkme = "0.2.3" 22 | remote-trait-object-macro = { version = "=0.4.1", path = "../remote-trait-object-macro"} 23 | 24 | [dev-dependencies] 25 | env_logger = "0.7.1" 26 | serde_json = "1.0" 27 | -------------------------------------------------------------------------------- /remote-trait-object/LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | -------------------------------------------------------------------------------- /remote-trait-object/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any person obtaining a copy 2 | of this software and associated documentation files (the "Software"), to deal 3 | in the Software without restriction, including without limitation the rights 4 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 5 | copies of the Software, and to permit persons to whom the Software is 6 | furnished to do so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in all 9 | copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 12 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 13 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 14 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 15 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 16 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 17 | SOFTWARE. 18 | -------------------------------------------------------------------------------- /remote-trait-object/README.md: -------------------------------------------------------------------------------- 1 | # remote-trait-object 2 | 3 | ![Build Status](https://github.com/CodeChain-io/intertrait/workflows/ci/badge.svg) 4 | [![License](https://img.shields.io/badge/license-MIT%20OR%20Apache--2.0-blue.svg)]( 5 | https://github.com/CodeChain-io/remote-trait-object#license) 6 | [![Cargo](https://img.shields.io/crates/v/remote-trait-object.svg)]( 7 | https://crates.io/crates/remote-trait-object) 8 | [![Documentation](https://docs.rs/remote-trait-object/badge.svg)]( 9 | https://docs.rs/remote-trait-object) 10 | [![chat](https://img.shields.io/discord/569610676205781012.svg?logo=discord)](https://discord.gg/xhpdXm7) 11 | 12 | `remote-trait-object` is a general, powerful, and simple [remote method invocation](https://en.wikipedia.org/wiki/Distributed_object_communication) library 13 | based on trait objects. 14 | 15 | It is... 16 | 17 | 1. Based on _services_ that can be exported and imported **as trait objects** - 18 | You register a service object, which is a trait object, and export it. On the other side, you import it into a proxy object, which is also a trait object. 19 | 1. Based on a point-to-point connection - All operations are conducted upon a single connection, which has **two ends**. 20 | 1. Easy to export and import services - During a remote method call in some service, you **can export and import another service as an argument or a return value** of the method. 21 | 1. Independent from the transport model - The transport model is abstracted and **users must provide a concrete implementation of it**. 22 | 1. Concurrent - you can both **call and handle remote calls concurrently**. 23 | 24 | See the [documentation](https://docs.rs/remote-trait-object). 25 | 26 | ## Example 27 | 28 | [This example code](https://github.com/CodeChain-io/remote-trait-object/blob/master/remote-trait-object-tests/src/simple.rs) 29 | briefly shows how you can use `remote-trait-object`. 30 | 31 | Note that `crate::transport::create()` is for creating the transport ends that are provided to `remote-trait-object ` contexts, which is just in-process communication for the test. 32 | You have to implement your own transport implementation if you're going to actually use this crate./ 33 | 34 | ```rust 35 | use remote_trait_object::*; 36 | 37 | #[service] 38 | pub trait CreditCard: Service { 39 | fn pay(&mut self, ammount: u64) -> Result<(), ()>; 40 | } 41 | struct SomeCreditCard { 42 | money: u64, 43 | } 44 | impl Service for SomeCreditCard {} 45 | impl CreditCard for SomeCreditCard { 46 | fn pay(&mut self, ammount: u64) -> Result<(), ()> { 47 | if ammount <= self.money { 48 | self.money -= ammount; 49 | Ok(()) 50 | } else { 51 | Err(()) 52 | } 53 | } 54 | } 55 | 56 | #[service] 57 | pub trait PizzaStore: Service { 58 | fn order_pizza(&self, credit_card: ServiceRef) -> Result; 59 | } 60 | struct SomePizzaStore; 61 | impl Service for SomePizzaStore {} 62 | impl PizzaStore for SomePizzaStore { 63 | fn order_pizza(&self, credit_card: ServiceRef) -> Result { 64 | let mut credit_card_proxy: Box = credit_card.unwrap_import().into_proxy(); 65 | credit_card_proxy.pay(10)?; 66 | Ok("Tasty Pizza".to_owned()) 67 | } 68 | } 69 | 70 | #[test] 71 | fn test() { 72 | let crate::transport::TransportEnds { 73 | recv1, 74 | send1, 75 | recv2, 76 | send2, 77 | } = crate::transport::create(); 78 | 79 | let _context_pizza_town = Context::with_initial_service_export( 80 | Config::default_setup(), 81 | send1, 82 | recv1, 83 | ServiceToExport::new(Box::new(SomePizzaStore) as Box), 84 | ); 85 | 86 | let (_context_customer, pizza_store): (_, ServiceToImport) = 87 | Context::with_initial_service_import(Config::default_setup(), send2, recv2); 88 | let pizza_store_proxy: Box = pizza_store.into_proxy(); 89 | 90 | let my_credit_card = Box::new(SomeCreditCard { 91 | money: 11, 92 | }) as Box; 93 | assert_eq!(pizza_store_proxy.order_pizza(ServiceRef::create_export(my_credit_card)).unwrap(), "Tasty Pizza"); 94 | 95 | let my_credit_card = Box::new(SomeCreditCard { 96 | money: 9, 97 | }) as Box; 98 | assert!(pizza_store_proxy.order_pizza(ServiceRef::create_export(my_credit_card)).is_err()); 99 | } 100 | ``` -------------------------------------------------------------------------------- /remote-trait-object/flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CodeChain-io/remote-trait-object/51480ea1635a7c88ad4a13a76d8133aa9110233f/remote-trait-object/flow.png -------------------------------------------------------------------------------- /remote-trait-object/src/context.rs: -------------------------------------------------------------------------------- 1 | use crate::packet::{PacketView, SlotType}; 2 | use crate::port::{client::Client, server::Server, BasicPort, Port}; 3 | use crate::transport::multiplex::{self, ForwardResult, MultiplexResult, Multiplexer}; 4 | use crate::transport::{TransportRecv, TransportSend}; 5 | use crate::{raw_exchange::*, Service, ServiceToExport, ServiceToImport}; 6 | use parking_lot::Mutex; 7 | use std::sync::{Arc, Weak}; 8 | use threadpool::ThreadPool; 9 | 10 | mod meta_service { 11 | use super::*; 12 | /// This is required because of macro 13 | use crate as remote_trait_object; 14 | 15 | #[remote_trait_object_macro::service] 16 | pub trait MetaService: Service {} 17 | 18 | pub struct MetaServiceImpl {} 19 | 20 | impl MetaServiceImpl { 21 | pub fn new() -> Self { 22 | Self {} 23 | } 24 | } 25 | 26 | impl Service for MetaServiceImpl {} 27 | 28 | impl MetaService for MetaServiceImpl {} 29 | } 30 | use meta_service::{MetaService, MetaServiceImpl}; 31 | 32 | /// A configuration of a `remote-trait-object` context. 33 | #[derive(Clone, Debug)] 34 | pub struct Config { 35 | /// A name that will be appended to the names of various threads spawned by `remote-trait-object`, for an easy debug. 36 | /// 37 | /// This can be helpful if you handle multiple contexts of `remote-trait-object`. 38 | pub name: String, 39 | 40 | /// Number of the maximum of concurrent calls. 41 | /// 42 | /// Value of this doesn't have anything to do with the number of threads that would be spawned. 43 | /// Having a large number of this wouldn't charge any cost except really small additional memory allocation. 44 | /// 45 | /// [`server_threads`]: ./struct.Config.html#field.server_threads 46 | pub call_slots: usize, 47 | 48 | /// A timeout for a remote method call. 49 | /// 50 | /// All remote method invocations through your proxy object and delete requests (that happens when you drop a proxy object) 51 | /// will have this timeout. If it exceeds, it will cause an error. 52 | /// 53 | /// Use `None` for to wait indefinitely. 54 | pub call_timeout: Option, 55 | 56 | /// A maximum number of services that this context can export. 57 | pub maximum_services_num: usize, 58 | 59 | /// A shared instance of a thread pool that will be used in call handling 60 | /// 61 | /// A `remote-trait-object` context will use this thread pool to handle an incoming method call. 62 | /// Size of this pool determines the maximum number of concurrent calls that the context can handle. 63 | /// Note that this pool is wrapped in `Arc`, which means that it can be possibly shared with other places. 64 | pub thread_pool: Arc>, 65 | } 66 | 67 | impl Config { 68 | pub fn default_setup() -> Self { 69 | Self { 70 | name: "my rto".to_owned(), 71 | call_slots: 512, 72 | maximum_services_num: 65536, 73 | call_timeout: Some(std::time::Duration::from_millis(1000)), 74 | 75 | thread_pool: Arc::new(Mutex::new(ThreadPool::new(8))), 76 | } 77 | } 78 | } 79 | 80 | /// One end of a `remote-trait-object` connection. 81 | /// 82 | /// If you establish a remote-trait-object connection, 83 | /// there must be two ends and each will be provided as a `Context` to each user on both sides. 84 | /// 85 | /// A context holds multiple things to function as a `remote-trait-object` connection end. 86 | /// Since the connection is symmetric, it manages both _server_ and _client_ toward the other end. 87 | /// It also manages a _registry_ that contains all exported services. 88 | /// The server will look up the registry to find a target object for handling an incoming method invocation. 89 | /// 90 | /// Note that `remote-trait-object` is a point-to-point connection protocol. 91 | /// Exporting & importing a service are always performed on a specific connection, 92 | /// which is toward the other side, or another instance of `Context`. 93 | /// 94 | /// If you created an instance of this, that means you have a connection that has been successfully established **once**, 95 | /// but is not guaranteed to be alive. 96 | /// If the other end (or the other `Context`) is closed, most operations performed on `Context` will just cause an error. 97 | pub struct Context { 98 | config: Config, 99 | multiplexer: Option, 100 | server: Option, 101 | port: Option>, 102 | meta_service: Option>, 103 | cleaned: bool, 104 | } 105 | 106 | impl std::fmt::Debug for Context { 107 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 108 | f.debug_struct("Context") 109 | .field("config", &self.config) 110 | .finish() 111 | } 112 | } 113 | 114 | impl Context { 115 | /// Creates a new context without any initial services. 116 | /// 117 | /// If you decide to use this, you have to exchange raw [`HandleToExchange`] at least once using a secondary transportation means. 118 | /// It is really rarely needed, so please consider introducing an initializing service as an initial service, to avoid any raw exchange. 119 | /// 120 | /// Please see [`with_initial_service()`] for a general explanation of creation of `Context`. 121 | /// 122 | /// [`with_initial_service()`]: ./struct.Context.html#method.with_initial_service 123 | pub fn new( 124 | config: Config, 125 | transport_send: S, 126 | transport_recv: R, 127 | ) -> Self { 128 | let null_to_export = crate::service::create_null_service(); 129 | let (ctx, _null_to_import): (Self, ServiceToImport) = 130 | Self::with_initial_service( 131 | config, 132 | transport_send, 133 | transport_recv, 134 | ServiceToExport::new(null_to_export), 135 | ); 136 | ctx 137 | } 138 | 139 | /// Creates a new context only exporting a service, but importing nothing. 140 | /// 141 | /// The other end's context must be initialized with `with_initial_service_import()`. 142 | /// Please see [`with_initial_service()`] for a general explanation of creation of `Context`. 143 | /// 144 | /// [`with_initial_service()`]: ./struct.Context.html#method.with_initial_service 145 | pub fn with_initial_service_export< 146 | S: TransportSend + 'static, 147 | R: TransportRecv + 'static, 148 | A: ?Sized + Service, 149 | >( 150 | config: Config, 151 | transport_send: S, 152 | transport_recv: R, 153 | initial_service: ServiceToExport, 154 | ) -> Self { 155 | let (ctx, _null_to_import): (Self, ServiceToImport) = 156 | Self::with_initial_service(config, transport_send, transport_recv, initial_service); 157 | ctx 158 | } 159 | 160 | /// Creates a new context only importing a service, but exporting nothing. 161 | /// 162 | /// The other end's context must be initialized with `with_initial_service_export()`. 163 | /// Please see [`with_initial_service()`] for a general explanation of creation of `Context`. 164 | /// 165 | /// [`with_initial_service()`]: ./struct.Context.html#method.with_initial_service 166 | pub fn with_initial_service_import< 167 | S: TransportSend + 'static, 168 | R: TransportRecv + 'static, 169 | B: ?Sized + Service, 170 | >( 171 | config: Config, 172 | transport_send: S, 173 | transport_recv: R, 174 | ) -> (Self, ServiceToImport) { 175 | let null_to_export = crate::service::create_null_service(); 176 | let (ctx, import) = Self::with_initial_service( 177 | config, 178 | transport_send, 179 | transport_recv, 180 | ServiceToExport::new(null_to_export), 181 | ); 182 | (ctx, import) 183 | } 184 | 185 | /// Creates a new context exchanging two services, one for export and one for import. 186 | /// 187 | /// It takes `initial_service` and registers in it, and passes a `HandleToExchange` internally. (_export_). 188 | /// Also it receives a `HandleToExchange` from the other side, and makes it into a proxy object. (_import_) 189 | /// 190 | /// The other end's context must be initialized with `with_initial_service()` as well, and 191 | /// such processes will be symmetric for both. 192 | /// 193 | /// [`HandleToExchange`]: ../raw_exchange/struct.HandleToExchange.html 194 | pub fn with_initial_service< 195 | S: TransportSend + 'static, 196 | R: TransportRecv + 'static, 197 | A: ?Sized + Service, 198 | B: ?Sized + Service, 199 | >( 200 | config: Config, 201 | transport_send: S, 202 | transport_recv: R, 203 | initial_service: ServiceToExport, 204 | ) -> (Self, ServiceToImport) { 205 | let MultiplexResult { 206 | multiplexer, 207 | request_recv, 208 | response_recv, 209 | } = Multiplexer::multiplex::(config.clone(), transport_recv); 210 | let transport_send = Arc::new(transport_send) as Arc; 211 | 212 | let client = Client::new( 213 | config.clone(), 214 | Arc::clone(&transport_send), 215 | Box::new(response_recv), 216 | ); 217 | let port = BasicPort::new( 218 | config.clone(), 219 | client, 220 | (Box::new(MetaServiceImpl::new()) as Box).into_skeleton(), 221 | initial_service.get_raw_export(), 222 | ); 223 | let server = Server::new( 224 | config.clone(), 225 | port.get_registry(), 226 | transport_send, 227 | Box::new(request_recv), 228 | ); 229 | 230 | let port_weak = Arc::downgrade(&port) as Weak; 231 | let meta_service = as ImportProxy>::import_proxy( 232 | Weak::clone(&port_weak), 233 | HandleToExchange(crate::forwarder::META_SERVICE_OBJECT_ID), 234 | ); 235 | let initial_handle = HandleToExchange(crate::forwarder::INITIAL_SERVICE_OBJECT_ID); 236 | 237 | let ctx = Context { 238 | config, 239 | multiplexer: Some(multiplexer), 240 | server: Some(server), 241 | port: Some(port), 242 | meta_service: Some(meta_service), 243 | cleaned: false, 244 | }; 245 | let initial_service = ServiceToImport::from_raw_import(initial_handle, port_weak); 246 | (ctx, initial_service) 247 | } 248 | 249 | pub(crate) fn get_port(&self) -> Weak { 250 | Arc::downgrade( 251 | &self 252 | .port 253 | .clone() 254 | .expect("It becomes None only when the context is dropped."), 255 | ) as Weak 256 | } 257 | 258 | /// Clears all service objects in its registry. 259 | /// 260 | /// The most usual way of deleting a service object is dropping its proxy object on the client side, and letting it request a delete to the exporter side. 261 | /// However, in some cases (especially while you're trying to shut down the connection) it is useful to clear all exported service objects 262 | /// **by the exporter side itself**. 263 | /// 264 | /// Note that it will cause an error if the client side drops a proxy object of an already deleted (by this method) service object. 265 | /// Consider calling [`disable_garbage_collection()`] on the other end if there's such an issue. 266 | /// 267 | /// Note also that this might trigger _delete request_ as a side effect since the service object might own a proxy object. 268 | /// 269 | /// [`disable_garbage_collection()`]: ./struct.Context.html#method.disable_garbage_collection 270 | pub fn clear_service_registry(&mut self) { 271 | self.port.as_mut().unwrap().clear_registry(); 272 | } 273 | 274 | /// Disables all _delete request_ from this end to the other end. 275 | /// 276 | /// If you call this, all `drop()` of proxy objects imported from this context won't send a delete request anymore. 277 | /// This is useful when you're not sure if the connection is still alive, but you have to close your side's context anyway. 278 | pub fn disable_garbage_collection(&self) { 279 | self.port 280 | .as_ref() 281 | .expect("It becomes None only when the context is dropped.") 282 | .set_no_drop(); 283 | } 284 | 285 | /// Waits until the transport is closed. 286 | /// 287 | /// Technically, this method will block until `TransportRecv` returns an error. 288 | /// Use this if you have nothing to do while the connection is working well. 289 | /// 290 | /// TODO: We should actually consider `timeout` 291 | pub fn wait(mut self, timeout: Option) -> Result<(), Self> { 292 | if let Err(multiplexer) = self 293 | .multiplexer 294 | .take() 295 | .expect("It becomes None only when the context is dropped.") 296 | .wait(timeout) 297 | { 298 | self.multiplexer.replace(multiplexer); 299 | return Err(self); 300 | } 301 | 302 | self.port.as_ref().unwrap().set_no_drop(); 303 | self.port.as_ref().unwrap().clear_registry(); 304 | drop(self.meta_service.take().unwrap()); 305 | 306 | self.cleaned = true; 307 | Ok(()) 308 | } 309 | } 310 | 311 | impl Drop for Context { 312 | /// This will delete all service objects after calling `disable_garbage_collection()` internally. 313 | fn drop(&mut self) { 314 | if !self.cleaned { 315 | self.multiplexer 316 | .take() 317 | .expect("It becomes None only when the context is dropped.") 318 | .shutdown(); 319 | // We have to clean all registered service, as some might hold another proxy object inside, which refers this context's port. 320 | // For such case, we have to make them be dropped first before we unwrap the Arc 321 | self.port.as_ref().unwrap().set_no_drop(); 322 | self.port.as_ref().unwrap().clear_registry(); 323 | drop(self.meta_service.take().unwrap()); 324 | } 325 | 326 | // Shutdown server after multiplexer 327 | self.server 328 | .take() 329 | .expect("It becomes None only when the context is dropped.") 330 | .shutdown(); 331 | // Shutdown port after multiplexer 332 | Arc::try_unwrap( 333 | self.port 334 | .take() 335 | .expect("It becomes None only when the context is dropped."), 336 | ) 337 | .unwrap() 338 | .shutdown(); 339 | } 340 | } 341 | 342 | pub struct PacketForward; 343 | 344 | impl multiplex::Forward for PacketForward { 345 | fn forward(packet: PacketView) -> ForwardResult { 346 | match packet.slot().get_type() { 347 | SlotType::Request => ForwardResult::Request, 348 | SlotType::Response => ForwardResult::Response, 349 | } 350 | } 351 | } 352 | -------------------------------------------------------------------------------- /remote-trait-object/src/forwarder.rs: -------------------------------------------------------------------------------- 1 | use crate::packet::PacketView; 2 | use crate::port::{null_weak_port, Handler, Port}; 3 | use crate::raw_exchange::Skeleton; 4 | use crate::service::Dispatch; 5 | use crate::Config; 6 | use parking_lot::RwLock; 7 | use std::collections::{HashMap, VecDeque}; 8 | use std::fmt; 9 | use std::sync::{Arc, Weak}; 10 | 11 | pub type ServiceObjectId = u32; 12 | pub const DELETE_REQUEST: crate::service::MethodId = std::u32::MAX; 13 | pub const META_SERVICE_OBJECT_ID: ServiceObjectId = 0; 14 | pub const INITIAL_SERVICE_OBJECT_ID: ServiceObjectId = 1; 15 | pub const NULL_ID: ServiceObjectId = std::u32::MAX; 16 | 17 | pub struct ServiceForwarder { 18 | service_objects: RwLock>>, 19 | available_ids: RwLock>, 20 | port: RwLock>, 21 | } 22 | 23 | impl fmt::Debug for ServiceForwarder { 24 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 25 | f.debug_list() 26 | .entries(self.service_objects.read().keys()) 27 | .finish() 28 | } 29 | } 30 | 31 | impl ServiceForwarder { 32 | pub fn new(config: Config, meta_service: Skeleton, service_object: Skeleton) -> Self { 33 | let service_objects: RwLock>> = 34 | Default::default(); 35 | service_objects 36 | .write() 37 | .insert(META_SERVICE_OBJECT_ID, meta_service.raw); 38 | service_objects 39 | .write() 40 | .insert(INITIAL_SERVICE_OBJECT_ID, service_object.raw); 41 | let mut available_ids = VecDeque::new(); 42 | for i in 0u32..(config.maximum_services_num as u32) { 43 | if i != META_SERVICE_OBJECT_ID && i != INITIAL_SERVICE_OBJECT_ID { 44 | available_ids.push_back(i); 45 | } 46 | } 47 | 48 | Self { 49 | service_objects, 50 | available_ids: RwLock::new(available_ids), 51 | port: RwLock::new(null_weak_port()), 52 | } 53 | } 54 | 55 | pub fn register_service_object(&self, service_object: Arc) -> ServiceObjectId { 56 | let id = self 57 | .available_ids 58 | .write() 59 | .pop_front() 60 | .expect("Too many service objects had been created"); 61 | assert!(self 62 | .service_objects 63 | .write() 64 | .insert(id, service_object) 65 | .is_none()); 66 | id 67 | } 68 | 69 | pub fn forward_and_call(&self, packet: PacketView) -> Vec { 70 | let object_id = packet.object_id(); 71 | let method = packet.method(); 72 | let data = packet.data(); 73 | 74 | if method == DELETE_REQUEST { 75 | self.delete(object_id); 76 | Vec::new() 77 | } else { 78 | crate::service::serde_support::port_thread_local::set_port(self.port.read().clone()); 79 | let handler = Arc::clone( 80 | self.service_objects 81 | .read() 82 | .get(&object_id) 83 | .unwrap_or_else(|| panic!("Fail to find {} from ServiceForwarder", object_id)), 84 | ); 85 | let result = handler.dispatch_and_call(method, data); 86 | crate::service::serde_support::port_thread_local::remove_port(); 87 | result 88 | } 89 | } 90 | 91 | pub fn clear(&self) { 92 | self.service_objects.write().clear(); 93 | // we don't restore available_ids here becuase clear() will be called in termination phase 94 | } 95 | 96 | fn delete(&self, id: ServiceObjectId) { 97 | self.service_objects.write().remove(&id).unwrap(); 98 | self.available_ids.write().push_back(id); 99 | } 100 | 101 | /// Be careful of this circular reference 102 | pub fn set_port(&self, port: Weak) { 103 | *self.port.write() = port 104 | } 105 | } 106 | 107 | impl Handler for ServiceForwarder { 108 | fn handle(&self, input: PacketView) -> Vec { 109 | self.forward_and_call(input) 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /remote-trait-object/src/lib.rs: -------------------------------------------------------------------------------- 1 | /*! 2 | `remote-trait-object` is a general, powerful, and simple [remote method invocation](https://en.wikipedia.org/wiki/Distributed_object_communication) library 3 | based on trait objects. 4 | 5 | It is... 6 | 7 | 1. Based on _services_ that can be exported and imported **as trait objects** - 8 | You register a service object, which is a trait object, and export it. On the other side, you import it into a proxy object, which is also a trait object. 9 | 1. Based on a point-to-point connection - All operations are conducted upon a single connection, which has **two ends**. 10 | 1. Easy to export and import services - During a remote method call in some service, you **can export and import another service as an argument or a return value** of the method. 11 | 1. Independent from the transport model - The transport model is abstracted and **users must provide a concrete implementation of it**. 12 | 1. Concurrent - you can both **call and handle remote calls concurrently**. 13 | 14 | Note that it is commonly abbreviated as **RTO**. 15 | 16 | ## Introduction 17 | 18 | **_Connection_** is a concept of a solid point-to-point pair where all operations of `remote-trait-object` are conducted. 19 | 20 | **_Context_** is one end of a connection, and one connection will have two instances of this. Each side will access the _connection_ via the _context_. 21 | This corresponds to [`Context`]. 22 | 23 | **_Service_** is a set of well-defined, coherent operations that can be provided for use by other code. 24 | All communication between two parties takes place only through _services_. 25 | 26 | **_Service object_** is the subject who provides a _service_. 27 | It is a trait object that is wrapped in a _skeleton_. 28 | The wrapping _skeleton_ will invoke its method to handle remote calls from the other side. 29 | You can use any trait object as a _service object_, as long as the trait is a service trait. 30 | You can even export your proxy object as a service object. 31 | 32 | **_Skeleton_** is a wrapper of a service object, which is registered on a _context_ and will be invoked with remote calls from its _proxy object_ from the _client_ side. 33 | This corresponds to [`ServiceToExport`] or [`Skeleton`]. 34 | 35 | **_Proxy object_** is the provided _service_. 36 | It is a trait object that is a proxy to the remote _service object_ on the _server_ side. You can call its methods just like a local object. 37 | A _proxy object_ corresponds to exactly one _skeleton_, and vice versa. 38 | If a proxy object is dropped, it will request its deletion on the server side. This is called _delete request_. 39 | With this, the server side's context will remove the skeleton if the client doesn't own its proxy anymore. 40 | 41 | **_Service trait_** is a trait that represents a `service`. 42 | It is for two trait objects (_service object_ and _proxy object_). 43 | Both can share an identical _service trait_, but might have different but compatible _service traits_ as well. 44 | 45 | **_Server_** side refers to one side (or one _context_) in which the _skeleton_ resides. 46 | When talking about service exchange, it is sometimes called the _exporter_ side. 47 | 48 | **_Client_** side refers to one side (or one _context_) in which the _proxy object_ resides. 49 | When talking about service exchange, it is sometimes called the _importer_ side. 50 | 51 | Note that the concept of _server_ and _client_ are for one _skeleton_-_proxy_ pair, not the whole _context_-_context_ pair itself. 52 | No context is either _server_ nor _client_ by itself, but can be referred as one when we say a particular _skeleton_-_proxy_ pair. 53 | 54 | **_Handle_** is an index-like identifier that corresponds to a particular _skeleton_ in the _server_ side's context. Each _proxy object_ is carrying one. 55 | This corresponds to [`ServiceToImport`] or [`HandleToExchange`]. 56 | 57 | **_Exporting_** a service object means 58 | wrapping it in a _skeleton_, 59 | registering that on the _server_ side's _context_, 60 | producing a _handle_ to it, 61 | and passing the _handle_ to the client side. 62 | Note that you won't go through all these processes unless you're using [`raw_exchange`] module. 63 | 64 | **_Importing_** a handle into a proxy object means creating an object that fulfills its method calls by remotely invoking the skeleton on the server side. 65 | It carries the handle to fill it in the packet to send, which is for identifying the skeleton that this proxy corresponds to. 66 | 67 | It is sometimes called an _exchange_ when referring to both _export_ and _import_. 68 | 69 | ## How It Works 70 | ![diagram](https://github.com/CodeChain-io/remote-trait-object/raw/master/remote-trait-object/flow.png) 71 | 72 | 1. User calls a method of _proxy object_ which is a trait object wrapped in a smart pointer. 73 | 2. The call will be delivered to the _context_ from which the _proxy object_ is imported, after serialized into a byte packet. 74 | Note that the actual transportation of data happens only at the _context_, which functions as a connection end. 75 | 3. The packet will be sent to the other end, (or context) by the _transport_. 76 | 4. After the other side's _context_ receives the packet, it forwards the packet to the target _skeleton_ in its registry. 77 | 5. The skeleton will dispatch the packet into an actual method call to the _service object_, which is a trait object wrapped in a smart pointer. 78 | 79 | The result will go back to the user, again via the contexts and transport. 80 | 81 | ## Smart Pointers 82 | Both service object and proxy object are trait objects like `dyn MyService`. 83 | To own and pass a trait object, it should be holded by some smart pointer. 84 | Currently `remote-trait-object` supports three types of smart pointers for service objects and proxy objects. 85 | 86 | 1. `Box` 87 | 2. `std::sync::Arc` 88 | 3. `std::sync::Arc>` 89 | 90 | When you export a service object, you can **export from whichever type** among them. 91 | 92 | On the other hand when you import a proxy object, you can **import into whichever type** among them. 93 | Choosing smart pointer types is completely independent for exporting & importing sides. 94 | Both can decide which to use, depending on their own requirements. 95 | 96 | **Exporter (server)** 97 | - Use `Box<>` when you have nothing to do with the object after you export it. 98 | It will be registered in the [`Context`], and will be alive until the corresponding proxy object is dropped. 99 | You can never access the object directly, since it will be _moved_ to the registry. 100 | 101 | - Use `Arc<>` when you have something to do with the object after you export it, by `Arc::clone()` and holding somewhere. 102 | In this case, both its proxy object and some `Arc` copy on the exporter side can access the object, 103 | though the latter can only access it immutably. 104 | With this a single _service object_ can be shared among multiple _skeletons_ while a _skeleton_ always matches to exactly one _service object_. 105 | 106 | - Use `Arc>` when you have to access the object **mutably**, in the similar situation with `Arc` case. 107 | 108 | **Importer (client)** 109 | - This is not different from the plain Rust programming. Choose whichever type you want depending on your use. 110 | 111 | Note that `Arc<>` will not be supported if the trait has a method that takes `&mut self`. 112 | You must use either `Box<>` or `Arc>` in such casse. 113 | 114 | 115 | ## Service Trait 116 | Service trait is the core idea of the `remote-trait-object`. Once you define a trait that you want to use it 117 | as a interface between two ends, you can put `#[remote_trait_object::service]` to make it as a service trait. 118 | It will generate all required code to construct a proxy and skeleton to it. 119 | 120 | ### Trait Requirements 121 | There are some rules to use a trait as a service trait. 122 | 123 | 1. Of course, the trait must be **object-safe** because it will be used as a trait object. 124 | 125 | 1. It can't have any type item. 126 | 127 | 1. No generic parameter (including lifetime) is allowed, in both trait definition and methods. 128 | 129 | 1. All types appeared in method parameter or return value must implement [`serde`]'s [`Serialize`] and [`Deserialize`]. 130 | This library performs de/serialization of data using [`serde`], though the data format can be chosen. 131 | Depending on your choice of macro arguments, this condition may differ slightly. See this [section](https://github.com/CodeChain-io/remote-trait-object) 132 | 133 | 1. You can't return a reference as a return type. 134 | This holds for a composite type too. For example, you can't return `&i32` nor `(i32, i32, &i32)`. 135 | 136 | 1. You can pass only first-order reference as a parameter. 137 | For example, you can pass `&T` only if the `T` doesn't a contain reference at all. 138 | Note that T must be `Sized`. There are two exceptions that accept `?Sized` `T`s: `str` and `[U]` where `U` doesn't contain reference at all. 139 | 140 | ### Example 141 | ``` 142 | use remote_trait_object as rto; 143 | 144 | #[remote_trait_object_macro::service] 145 | pub trait PizzaStore : rto::Service { 146 | fn order_pizza(&mut self, menu: &str, money: u64); 147 | fn ask_pizza_price(&self, menu: &str) -> u64; 148 | } 149 | ``` 150 | 151 | ### Service Compatibility 152 | Although it is common to use the same trait for both proxy object and service object, it is possible to import a service into another trait. 153 | 154 | TODO: We have not strictly designed the compatibility model but will be provided in the next version. 155 | 156 | Roughly, in current version, trait `P` is considered to be compatible to be proxy of trait `S`, only if 157 | 1. `P` has exactly the same methods as `S` declared in the same order, that differ only in types of parameter and return value. 158 | 2. Such different types must be compatible. 159 | 3. Types are considered to be compatible if both are serialized and deserialized with exactly the same value. 160 | 161 | `remote-trait-object` always guarantees 3. between [`ServiceToExport`], [`ServiceToImport`] and [`ServiceRef`]. 162 | 163 | ## Export & Import services 164 | One of the core features of `remote-trait-object` is its simple and straightforward but extensive export & import of services. 165 | Of course this library doesn't make you manually register a service object, passing handle and so on, but provides you a much simpler and abstracted way. 166 | 167 | There are three ways of exporting and importing a service. 168 | 169 | ### During Initialization 170 | When you create new `remote-trait-object` contexts, you can export and import one as initial services. 171 | See details [here](./struct.Context.html#method.with_initial_service) 172 | 173 | ### As a Parameter or a Return Value 174 | This is the most common way of exporting / importing services. 175 | 176 | See [`ServiceToExport`], [`ServiceToImport`] and [`ServiceRef`] for more. 177 | 178 | ### Raw Exchange 179 | You will be **rarely** needed to perform a service exchange using a raw _skeleton_ and _handle_. 180 | If you use this method, you will do basically the same thing as what the above methods would do internally, but have some extra controls over it. 181 | Raw exchange is not that frequently required. In most cases using only method 1. and 2. will be sufficient. 182 | 183 | See the [module-level documentation](./raw_exchange/index.html) for more. 184 | 185 | ## Example 186 | ```ignore 187 | use remote_trait_object::*; 188 | 189 | #[service] 190 | pub trait CreditCard: Service { 191 | fn pay(&mut self, ammount: u64) -> Result<(), ()>; 192 | } 193 | struct SomeCreditCard { money: u64 } 194 | impl CreditCard for SomeCreditCard { 195 | fn pay(&mut self, ammount: u64) -> Result<(), ()> { 196 | if ammount <= self.money { 197 | self.money -= ammount; 198 | Ok(()) 199 | } else { Err(()) } 200 | } 201 | } 202 | 203 | #[service] 204 | pub trait PizzaStore: Service { 205 | fn order_pizza(&self, credit_card: ServiceRef) -> Result; 206 | } 207 | struct SomePizzaStore; 208 | impl PizzaStore for SomePizzaStore { 209 | fn order_pizza(&self, credit_card: ServiceRef) -> Result { 210 | let mut credit_card_proxy: Box = credit_card.unwrap_import().into_proxy(); 211 | credit_card_proxy.pay(10)?; 212 | Ok("Tasty Pizza".to_owned()) 213 | } 214 | } 215 | 216 | // PROGRAM 1 217 | let (send, recv) = unimplemented!("Implement your own transport medium and provide here!") 218 | let _context_pizza_town = Context::with_initial_service_export( 219 | Config::default_setup(), send, recv, 220 | ServiceToExport::new(Box::new(SomePizzaStore) as Box), 221 | ); 222 | 223 | // PROGRAM 2 224 | let (send, recv) = unimplemented!("Implement your own transport medium and provide here!") 225 | let (_context_customer, pizza_store): (_, ServiceToImport) = 226 | Context::with_initial_service_import(Config::default_setup(), send, recv); 227 | let pizza_store_proxy: Box = pizza_store.into_proxy(); 228 | 229 | let my_credit_card = Box::new(SomeCreditCard {money: 11}) as Box; 230 | assert_eq!(pizza_store_proxy.order_pizza( 231 | ServiceRef::create_export(my_credit_card)).unwrap(), "Tasty Pizza"); 232 | 233 | let my_credit_card = Box::new(SomeCreditCard {money: 9}) as Box; 234 | assert!(pizza_store_proxy.order_pizza( 235 | ServiceRef::create_export(my_credit_card)).is_err()); 236 | ``` 237 | You can check the working code of this example [here](https://github.com/CodeChain-io/remote-trait-object/blob/master/remote-trait-object-tests/src/simple.rs). 238 | 239 | See more examples [here](https://github.com/CodeChain-io/remote-trait-object/tree/master/remote-trait-object-tests/src). 240 | 241 | [`Arc`]: https://doc.rust-lang.org/std/sync/struct.Arc.html 242 | [`Skeleton`]: ./raw_exchange/struct.Skeleton.html 243 | [`HandleToExchange`]: ./raw_exchange/struct.HandleToExchange.html 244 | [`Serialize`]: https://docs.serde.rs/serde/trait.Serialize.html 245 | [`Deserialize`]: https://docs.serde.rs/serde/trait.Deserialize.html 246 | */ 247 | 248 | #[macro_use] 249 | extern crate log; 250 | 251 | mod context; 252 | mod forwarder; 253 | mod packet; 254 | mod port; 255 | mod queue; 256 | mod service; 257 | #[cfg(test)] 258 | mod tests; 259 | pub mod transport; 260 | 261 | pub use context::{Config, Context}; 262 | pub use service::id::setup_identifiers; 263 | pub use service::serde_support::{ServiceRef, ServiceToExport, ServiceToImport}; 264 | pub use service::{SerdeFormat, Service}; 265 | 266 | pub mod raw_exchange { 267 | //! This module is needed only if you want to perform some raw exchange (or export/import) of services. 268 | //! 269 | //! You may have [`Skeleton`], which is a service to be registered, but **with its trait erased**. 270 | //! You can prepare one and hold it for a while, and register it on demand. 271 | //! Creating an instance of [`Skeleton`] doesn't involve any context. 272 | //! That means you can have a service object that both its trait and its context (to be exported later) remains undecided. 273 | //! 274 | //! You may also have [`HandleToExchange`], which is a raw handle as a result of exporting a [`Skeleton`]. 275 | //! It should be imported as a proxy object on the other side, but you can manage it freely until that moment. 276 | //! It is useful when there is a third party besides two contexts of a single connection, who wants to perform service exchange by itself, not directly between contexts. 277 | //! 278 | //! Raw exchange is not that frequently required. In most cases just using ordinary methods like [`ServiceToExport`], [`ServiceToImport`] or [`ServiceRef`] would be enough. 279 | //! Please check again that you surely need this module. 280 | //! 281 | //! [`ServiceToExport`]: ../struct.ServiceToExport.html 282 | //! [`ServiceToImport`]: ../struct.ServiceToImport.html 283 | //! [`ServiceRef`]: ../enum.ServiceRef.html 284 | 285 | pub use crate::service::export_import::{ 286 | export_service_into_handle, import_null_proxy, import_service_from_handle, 287 | HandleToExchange, ImportProxy, IntoSkeleton, Skeleton, 288 | }; 289 | } 290 | 291 | #[doc(hidden)] 292 | pub mod macro_env { 293 | pub use super::raw_exchange::*; 294 | pub use super::service::export_import::{get_dispatch, FromSkeleton}; 295 | pub use super::*; 296 | pub use port::Port; 297 | pub use service::export_import::create_skeleton; 298 | pub use service::id::{IdMap, MethodIdAtomic, ID_ORDERING, MID_REG}; 299 | pub use service::{Cbor as DefaultSerdeFormat, Dispatch, Handle, MethodId}; 300 | pub use SerdeFormat; 301 | } 302 | 303 | // Re-export macro 304 | pub use remote_trait_object_macro::*; 305 | -------------------------------------------------------------------------------- /remote-trait-object/src/packet.rs: -------------------------------------------------------------------------------- 1 | use crate::forwarder::ServiceObjectId; 2 | use crate::service::MethodId; 3 | use serde::{Deserialize, Serialize}; 4 | use std::fmt; 5 | 6 | const UNDECIDED_SLOT: u32 = 4_294_967_295; 7 | 8 | #[derive(Serialize, Deserialize, Debug, Copy, Clone)] 9 | pub struct SlotId(u32); 10 | 11 | impl fmt::Display for SlotId { 12 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 13 | write!( 14 | f, 15 | "SlotId {{ id: {:?}, type: {:?} }}", 16 | self.0, 17 | self.get_type() 18 | ) 19 | } 20 | } 21 | 22 | #[derive(Debug)] 23 | pub enum SlotType { 24 | Request, 25 | Response, 26 | } 27 | 28 | impl SlotId { 29 | pub fn new(num: u32) -> Self { 30 | Self(num) 31 | } 32 | 33 | pub fn new_request() -> Self { 34 | Self(UNDECIDED_SLOT) 35 | } 36 | 37 | pub fn get_type(&self) -> SlotType { 38 | if self.0 >= SLOT_CALL_OR_RETURN_INDICATOR.0 { 39 | SlotType::Request 40 | } else { 41 | SlotType::Response 42 | } 43 | } 44 | 45 | pub fn change_to_response(&mut self) { 46 | assert!(self.0 >= SLOT_CALL_OR_RETURN_INDICATOR.0); 47 | self.0 -= SLOT_CALL_OR_RETURN_INDICATOR.0; 48 | } 49 | 50 | pub fn into_request(self) -> Self { 51 | assert!(self.0 < SLOT_CALL_OR_RETURN_INDICATOR.0); 52 | Self(self.0 + SLOT_CALL_OR_RETURN_INDICATOR.0) 53 | } 54 | 55 | pub fn as_usize(&self) -> usize { 56 | self.0 as usize 57 | } 58 | 59 | pub fn as_raw(&self) -> u32 { 60 | self.0 61 | } 62 | } 63 | 64 | const SLOT_CALL_OR_RETURN_INDICATOR: SlotId = SlotId(2_147_483_648); 65 | 66 | /// FIXME: Replace this hard-coded value to some constant evaluation 67 | const PACKET_HEADER_SIZE: usize = 12; 68 | 69 | #[test] 70 | fn packet_header_size() { 71 | let x = PacketHeader { 72 | slot: SlotId(0), 73 | service_object_id: 0, 74 | method: 0, 75 | }; 76 | assert_eq!(bincode::serialize(&x).unwrap().len(), PACKET_HEADER_SIZE); 77 | } 78 | 79 | #[derive(Serialize, Deserialize)] 80 | struct PacketHeader { 81 | pub slot: SlotId, 82 | pub service_object_id: ServiceObjectId, 83 | pub method: MethodId, 84 | } 85 | 86 | impl PacketHeader { 87 | pub const fn len() -> usize { 88 | PACKET_HEADER_SIZE 89 | } 90 | 91 | pub fn new(slot: SlotId, service_object_id: ServiceObjectId, method: MethodId) -> Self { 92 | PacketHeader { 93 | slot, 94 | service_object_id, 95 | method, 96 | } 97 | } 98 | 99 | pub fn from_buffer(buffer: &[u8]) -> Self { 100 | bincode::deserialize_from(buffer).unwrap() 101 | } 102 | 103 | pub fn write(&self, buffer: &mut [u8]) { 104 | bincode::serialize_into(buffer, self).unwrap() 105 | } 106 | } 107 | 108 | #[derive(Debug)] 109 | pub struct PacketView<'a> { 110 | buffer: &'a [u8], 111 | } 112 | 113 | impl<'a> fmt::Display for PacketView<'a> { 114 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 115 | write!( 116 | f, 117 | "Packet {{ slot: {}, object id: {}, method: {} }}", 118 | self.slot(), 119 | self.object_id(), 120 | self.method() 121 | ) 122 | } 123 | } 124 | 125 | impl<'a> PacketView<'a> { 126 | pub fn new(buffer: &'a [u8]) -> Self { 127 | Self { buffer } 128 | } 129 | 130 | pub fn header(&self) -> &'a [u8] { 131 | &self.buffer[0..PacketHeader::len()] 132 | } 133 | 134 | pub fn data(&self) -> &'a [u8] { 135 | &self.buffer[PacketHeader::len()..] 136 | } 137 | 138 | pub fn slot(&self) -> SlotId { 139 | let header = PacketHeader::from_buffer(self.buffer); 140 | header.slot 141 | } 142 | 143 | pub fn object_id(&self) -> ServiceObjectId { 144 | PacketHeader::from_buffer(self.buffer).service_object_id 145 | } 146 | 147 | pub fn method(&self) -> MethodId { 148 | let header = PacketHeader::from_buffer(self.buffer); 149 | header.method 150 | } 151 | 152 | pub fn to_vec(&self) -> Vec { 153 | self.buffer.to_vec() 154 | } 155 | 156 | pub fn to_owned(&self) -> Packet { 157 | Packet::new_from_buffer(self.buffer.to_vec()) 158 | } 159 | } 160 | 161 | #[derive(Debug)] 162 | pub struct Packet { 163 | buffer: Vec, 164 | } 165 | 166 | impl fmt::Display for Packet { 167 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 168 | let view = self.view(); 169 | fmt::Display::fmt(&view, f) 170 | } 171 | } 172 | 173 | impl Packet { 174 | pub fn new_from_buffer(buffer: Vec) -> Self { 175 | Self { buffer } 176 | } 177 | 178 | pub fn new_response_from_request(request: PacketView) -> Self { 179 | let buffer = request.header().to_vec(); 180 | let mut packet = Self { buffer }; 181 | 182 | let mut header = packet.header(); 183 | header.slot.change_to_response(); 184 | header.write(&mut packet.buffer); 185 | 186 | packet 187 | } 188 | 189 | pub fn new_request(service_object_id: ServiceObjectId, method: MethodId, args: &[u8]) -> Self { 190 | let mut buffer = vec![0_u8; PacketHeader::len() + args.len()]; 191 | let header = PacketHeader::new(SlotId::new_request(), service_object_id, method); 192 | header.write(&mut buffer); 193 | buffer[PacketHeader::len()..].copy_from_slice(args); 194 | Self { buffer } 195 | } 196 | 197 | pub fn buffer(&self) -> &[u8] { 198 | &self.buffer 199 | } 200 | 201 | pub fn view(&self) -> PacketView { 202 | PacketView::new(&self.buffer) 203 | } 204 | 205 | fn header(&mut self) -> PacketHeader { 206 | PacketHeader::from_buffer(&self.buffer) 207 | } 208 | 209 | pub fn data(&self) -> &[u8] { 210 | self.view().data() 211 | } 212 | 213 | // FIXME: Use Cursor to reduce data copy 214 | pub fn append_data(&mut self, data: &[u8]) { 215 | self.buffer.extend_from_slice(data); 216 | } 217 | 218 | pub fn set_slot(&mut self, slot_id: SlotId) { 219 | let mut header = self.header(); 220 | header.slot = slot_id; 221 | header.write(&mut self.buffer); 222 | } 223 | 224 | pub fn into_vec(self) -> Vec { 225 | self.buffer 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /remote-trait-object/src/port.rs: -------------------------------------------------------------------------------- 1 | pub mod client; 2 | pub mod server; 3 | pub mod types; 4 | 5 | pub use self::types::Handler; 6 | use crate::forwarder::ServiceForwarder; 7 | use crate::forwarder::{ServiceObjectId, DELETE_REQUEST}; 8 | use crate::packet::{Packet, PacketView}; 9 | use crate::raw_exchange::{HandleToExchange, Skeleton}; 10 | use crate::service::*; 11 | use crate::Config; 12 | use client::Client; 13 | use std::sync::{ 14 | atomic::{AtomicBool, Ordering}, 15 | Arc, Weak, 16 | }; 17 | 18 | pub trait Port: std::fmt::Debug + Send + Sync + 'static { 19 | fn call(&self, packet: PacketView) -> Packet; 20 | fn delete_request(&self, id: ServiceObjectId); 21 | fn register_service(&self, service_object: Arc) -> HandleToExchange; 22 | } 23 | 24 | /// Weak::new() is not implemented for ?Sized. 25 | /// See https://github.com/rust-lang/rust/issues/50513 26 | pub fn null_weak_port() -> Weak { 27 | Weak::::new() as Weak 28 | } 29 | 30 | #[derive(Debug)] 31 | pub struct BasicPort { 32 | registry: Arc, 33 | /// client is None only in the drop function. 34 | client: Option, 35 | /// If this is on, the port will not request delete 36 | /// This is useful when the port-port connection is terminating and you don't really 37 | /// care about the garabage collection. 38 | no_drop: AtomicBool, 39 | } 40 | 41 | impl Port for BasicPort { 42 | fn call(&self, packet: PacketView) -> Packet { 43 | self.client.as_ref().unwrap().call(packet) 44 | } 45 | 46 | fn delete_request(&self, id: ServiceObjectId) { 47 | if self.no_drop.load(Ordering::SeqCst) { 48 | return; 49 | } 50 | let packet = Packet::new_request(id, DELETE_REQUEST, &[]); 51 | assert!(self 52 | .client 53 | .as_ref() 54 | .unwrap() 55 | .call(packet.view()) 56 | .data() 57 | .is_empty()); 58 | } 59 | 60 | fn register_service(&self, service_object: Arc) -> HandleToExchange { 61 | HandleToExchange(self.registry.register_service_object(service_object)) 62 | } 63 | } 64 | 65 | impl BasicPort { 66 | pub fn new( 67 | config: Config, 68 | client: Client, 69 | meta_sevice: Skeleton, 70 | initial_service: Skeleton, 71 | ) -> Arc { 72 | let arc = Arc::new(Self { 73 | registry: Arc::new(ServiceForwarder::new(config, meta_sevice, initial_service)), 74 | client: Some(client), 75 | no_drop: AtomicBool::new(false), 76 | }); 77 | let arc2 = arc.clone() as Arc; 78 | arc.registry.set_port(Arc::downgrade(&arc2)); 79 | arc 80 | } 81 | 82 | pub fn get_registry(&self) -> Arc { 83 | self.registry.clone() 84 | } 85 | 86 | pub fn clear_registry(&self) { 87 | self.registry.clear(); 88 | } 89 | 90 | /// Please call shutdown after Multiplexer::shutdown 91 | pub fn shutdown(mut self) { 92 | self.client.take().unwrap().shutdown(); 93 | } 94 | 95 | pub fn set_no_drop(&self) { 96 | self.no_drop.store(true, Ordering::SeqCst); 97 | } 98 | } 99 | 100 | impl Drop for BasicPort { 101 | fn drop(&mut self) { 102 | assert!(self.client.is_none(), "Please call shutdown"); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /remote-trait-object/src/port/client.rs: -------------------------------------------------------------------------------- 1 | use crate::packet::{Packet, PacketView, SlotId}; 2 | use crate::queue::Queue; 3 | use crate::transport::{TransportError, TransportRecv, TransportSend}; 4 | use crate::Config; 5 | use crossbeam::channel::RecvTimeoutError::{Disconnected, Timeout}; 6 | use crossbeam::channel::{bounded, Receiver, Sender}; 7 | use std::sync::Arc; 8 | use std::thread; 9 | use std::time; 10 | 11 | /// CallSlot represents an instance of call to the another module 12 | #[derive(Debug)] 13 | struct CallSlot { 14 | id: SlotId, 15 | response: Receiver>, 16 | } 17 | 18 | #[derive(Debug)] 19 | pub struct Client { 20 | config: Config, 21 | call_slots: Arc>, 22 | transport_send: Arc, 23 | receiver_thread: Option>, 24 | joined_event_receiver: Receiver<()>, 25 | } 26 | 27 | impl Client { 28 | pub fn new( 29 | config: Config, 30 | transport_send: Arc, 31 | transport_recv: Box, 32 | ) -> Self { 33 | let (joined_event_sender, joined_event_receiver) = bounded(1); 34 | let callslot_size = SlotId::new(config.call_slots as u32); 35 | let call_slots = Arc::new(Queue::new(callslot_size.as_usize())); 36 | let mut to_slot_receivers = Vec::with_capacity(callslot_size.as_usize()); 37 | 38 | for i in 0..callslot_size.as_raw() { 39 | let (send_to_slot_recv, recv_for_slot) = bounded(1); 40 | call_slots 41 | .push(CallSlot { 42 | id: SlotId::new(i), 43 | response: recv_for_slot, 44 | }) 45 | .expect("Client does not call close"); 46 | to_slot_receivers.push(send_to_slot_recv); 47 | } 48 | 49 | let name = config.name.clone(); 50 | 51 | Client { 52 | config, 53 | call_slots, 54 | transport_send, 55 | receiver_thread: Some( 56 | thread::Builder::new() 57 | .name(format!("[{}] client", name)) 58 | .spawn(move || { 59 | receive_loop(transport_recv, to_slot_receivers); 60 | joined_event_sender.send(()).unwrap(); 61 | }) 62 | .unwrap(), 63 | ), 64 | joined_event_receiver, 65 | } 66 | } 67 | 68 | pub fn call(&self, packet: PacketView) -> Packet { 69 | // TODO: handle the error 70 | let slot = self 71 | .call_slots 72 | .pop(self.config.call_timeout) 73 | .expect("Too many calls on port"); 74 | 75 | let packet = { 76 | let mut packet = packet.to_owned(); 77 | packet.set_slot(slot.id.into_request()); 78 | packet 79 | }; 80 | 81 | // TODO: handle the error 82 | self.transport_send 83 | .send(packet.buffer(), self.config.call_timeout) 84 | .unwrap(); 85 | let response_packet = slot.response.recv().expect( 86 | "counterparty send is managed by client. \n\ 87 | This error might be due to drop after disconnection of the two remote-trait-object contexts. \n\ 88 | Please consider disable_garbage_collection() or explicit drop for the imported services.", 89 | ); 90 | 91 | self.call_slots 92 | .push(slot) 93 | .expect("Client does not close the queue"); 94 | 95 | // TODO: handle the error 96 | response_packet.unwrap() 97 | } 98 | 99 | pub fn shutdown(&mut self) { 100 | match self 101 | .joined_event_receiver 102 | .recv_timeout(time::Duration::from_millis(100)) 103 | { 104 | Err(Timeout) => { 105 | panic!( 106 | "There may be a deadlock or misuse of Client. Call Client::shutdown after Multiplexer::shutdown" 107 | ); 108 | } 109 | Err(Disconnected) => { 110 | panic!("Maybe receive_loop thread panics"); 111 | } 112 | Ok(_) => {} 113 | } 114 | self.receiver_thread.take().unwrap().join().unwrap(); 115 | } 116 | } 117 | 118 | impl Drop for Client { 119 | fn drop(&mut self) { 120 | assert!(self.receiver_thread.is_none(), "Please call shutdown"); 121 | } 122 | } 123 | 124 | fn receive_loop( 125 | transport_recv: Box, 126 | to_slot_receivers: Vec>>, 127 | ) { 128 | loop { 129 | match transport_recv.recv(None) { 130 | Ok(x) => { 131 | let packet = Packet::new_from_buffer(x); 132 | let slot_id = packet.view().slot(); 133 | to_slot_receivers[slot_id.as_usize()] 134 | .send(Ok(packet)) 135 | .expect("Slot receivers are managed in Client. Client must be dropped after this thread"); 136 | } 137 | Err(TransportError::Termination) => return, 138 | Err(_err) => { 139 | // TODO: Broadcast the error to all **active** call slots 140 | return; 141 | } 142 | }; 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /remote-trait-object/src/port/server.rs: -------------------------------------------------------------------------------- 1 | use super::types::Handler; 2 | use crate::packet::Packet; 3 | use crate::transport::{TransportError, TransportRecv, TransportSend}; 4 | use crate::Config; 5 | use crossbeam::channel::RecvTimeoutError::{Disconnected, Timeout}; 6 | use crossbeam::channel::{self, Receiver}; 7 | use std::sync::atomic::{AtomicI32, Ordering}; 8 | use std::sync::Arc; 9 | use std::thread; 10 | use std::time; 11 | 12 | pub struct Server { 13 | receiver_thread: Option>, 14 | joined_event_receiver: Receiver<()>, 15 | } 16 | 17 | impl Server { 18 | pub fn new( 19 | config: Config, 20 | handler: Arc, 21 | transport_send: Arc, 22 | transport_recv: Box, 23 | ) -> Self 24 | where 25 | H: Handler + Send + 'static, 26 | { 27 | let (joined_event_sender, joined_event_receiver) = channel::bounded(1); 28 | let receiver_thread = thread::Builder::new() 29 | .name(format!("[{}] port server receiver", config.name)) 30 | .spawn(move || { 31 | receiver(config, handler, transport_send, transport_recv); 32 | joined_event_sender 33 | .send(()) 34 | .expect("Server will be dropped after thread is joined"); 35 | }) 36 | .unwrap(); 37 | 38 | Server { 39 | receiver_thread: Some(receiver_thread), 40 | joined_event_receiver, 41 | } 42 | } 43 | 44 | pub fn shutdown(mut self) { 45 | match self 46 | .joined_event_receiver 47 | .recv_timeout(time::Duration::from_millis(500)) 48 | { 49 | Err(Timeout) => { 50 | panic!( 51 | "There may be a deadlock or misuse of Server. Call Server::shutdown when transport_recv is closed" 52 | ); 53 | } 54 | Err(Disconnected) => { 55 | panic!("Maybe receiver thread panics"); 56 | } 57 | Ok(_) => {} 58 | } 59 | 60 | self.receiver_thread.take().unwrap().join().unwrap(); 61 | } 62 | } 63 | 64 | impl Drop for Server { 65 | fn drop(&mut self) { 66 | assert!(self.receiver_thread.is_none(), "Please call shutdown") 67 | } 68 | } 69 | 70 | fn handle_single_call( 71 | packet: Packet, 72 | handler: Arc, 73 | transport_send: Arc, 74 | count: Arc, 75 | ) { 76 | let response = handler.handle(packet.view()); 77 | let mut response_packet = Packet::new_response_from_request(packet.view()); 78 | response_packet.append_data(&response); 79 | if let Err(_err) = transport_send.send(response_packet.buffer(), None) { 80 | // TODO: report the error to the context 81 | count.fetch_sub(1, Ordering::Release); 82 | return; 83 | }; 84 | count.fetch_sub(1, Ordering::Release); 85 | } 86 | 87 | fn receiver( 88 | config: Config, 89 | handler: Arc, 90 | transport_send: Arc, 91 | transport_recv: Box, 92 | ) where 93 | H: Handler + 'static, 94 | { 95 | let count = Arc::new(AtomicI32::new(0)); 96 | loop { 97 | match transport_recv.recv(None) { 98 | Ok(request) => { 99 | let packet = Packet::new_from_buffer(request); 100 | let handler = Arc::clone(&handler); 101 | let transport_send = Arc::clone(&transport_send); 102 | 103 | count.fetch_add(1, Ordering::Release); 104 | let count = Arc::clone(&count); 105 | config 106 | .thread_pool 107 | .lock() 108 | .execute(move || handle_single_call(packet, handler, transport_send, count)); 109 | } 110 | Err(TransportError::Termination) => break, 111 | Err(_err) => { 112 | // TODO: report this error to the context 113 | break; 114 | } 115 | } 116 | } 117 | // transport_recv is terminated. 118 | 119 | // TODO: handle too many loops 120 | while count.load(Ordering::Acquire) != 0 { 121 | thread::sleep(std::time::Duration::from_millis(1)); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /remote-trait-object/src/port/types.rs: -------------------------------------------------------------------------------- 1 | use crate::packet::PacketView; 2 | 3 | pub trait Handler: Send + Sync { 4 | fn handle(&self, input: PacketView) -> Vec; 5 | } 6 | 7 | impl Handler for F 8 | where 9 | F: Fn(PacketView) -> Vec + Send + Sync, 10 | { 11 | fn handle(&self, input: PacketView) -> Vec { 12 | self(input) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /remote-trait-object/src/queue.rs: -------------------------------------------------------------------------------- 1 | use crossbeam::channel::{ 2 | bounded, Receiver, 3 | RecvTimeoutError::{Disconnected, Timeout}, 4 | Sender, 5 | }; 6 | use parking_lot::Mutex; 7 | 8 | /// Blocking concurrent Queue. (Crossbeam's queue doens't block) 9 | /// Please use the queue with Arc 10 | #[derive(Debug)] 11 | pub struct Queue { 12 | // sender is None only when the close is called 13 | sender: Mutex>>, 14 | // receiver is None only when the close is called 15 | recver: Mutex>>, 16 | } 17 | 18 | impl Queue { 19 | pub fn new(size: usize) -> Self { 20 | let (sender, recver) = bounded(size); 21 | Queue { 22 | sender: Mutex::new(Some(sender)), 23 | recver: Mutex::new(Some(recver)), 24 | } 25 | } 26 | 27 | pub fn push(&self, x: T) -> Result<(), QueueClosed> { 28 | let guard = self.sender.lock(); 29 | let sender = guard.as_ref().ok_or(QueueClosed)?; 30 | sender.send(x).map_err(|_| QueueClosed) 31 | } 32 | pub fn pop(&self, timeout: Option) -> Result { 33 | let guard = self.recver.lock(); 34 | let recver = guard.as_ref().ok_or(PopError::QueueClosed)?; 35 | if let Some(duration) = timeout { 36 | recver.recv_timeout(duration).map_err(|err| match err { 37 | Timeout => PopError::Timeout, 38 | Disconnected => PopError::QueueClosed, 39 | }) 40 | } else { 41 | recver.recv().map_err(|_| PopError::QueueClosed) 42 | } 43 | } 44 | } 45 | 46 | #[derive(Debug)] 47 | pub struct QueueClosed; 48 | 49 | #[derive(Debug)] 50 | pub enum PopError { 51 | Timeout, 52 | QueueClosed, 53 | } 54 | -------------------------------------------------------------------------------- /remote-trait-object/src/service.rs: -------------------------------------------------------------------------------- 1 | pub mod export_import; 2 | pub mod handle; 3 | pub mod id; 4 | mod null; 5 | pub mod serde_support; 6 | 7 | use crate::forwarder::ServiceObjectId; 8 | use crate::port::Port; 9 | use std::sync::Weak; 10 | 11 | pub use handle::Handle; 12 | pub use null::{create_null_service, NullService}; 13 | pub type MethodId = u32; 14 | 15 | /// Exporter sides's interface to the service object. This will be implemented 16 | /// by each service trait's unique wrapper in the macro 17 | pub trait Dispatch: Send + Sync { 18 | fn dispatch_and_call(&self, method: MethodId, args: &[u8]) -> Vec; 19 | } 20 | 21 | impl Dispatch for F 22 | where 23 | F: Fn(MethodId, &[u8]) -> Vec + Send + Sync, 24 | { 25 | fn dispatch_and_call(&self, method: MethodId, args: &[u8]) -> Vec { 26 | self(method, args) 27 | } 28 | } 29 | 30 | /// The `Service` trait is a marker that is used as a supertrait for a service trait, 31 | /// indicating that the trait is for a service. 32 | /// 33 | /// It is bound to `Send` and `Sync`, and that's all. 34 | /// Please put this as a supertrait for every service trait, and implement it 35 | /// for all concrete service implementers. 36 | /// 37 | /** 38 | ## Example 39 | ``` 40 | use remote_trait_object::*; 41 | 42 | #[service] 43 | pub trait Piano: Service { 44 | fn play(&mut self); 45 | } 46 | 47 | struct Steinway; 48 | impl Service for Steinway {} 49 | impl Piano for Steinway { 50 | fn play(&mut self) { 51 | println!("Do Re Mi"); 52 | } 53 | } 54 | ``` 55 | **/ 56 | pub trait Service: Send + Sync {} 57 | 58 | /// A serde de/serialization format that will be used for a service. 59 | pub trait SerdeFormat { 60 | #[allow(clippy::result_unit_err)] 61 | fn to_vec(s: &S) -> Result, ()>; 62 | #[allow(clippy::result_unit_err)] 63 | fn from_slice(data: &[u8]) -> Result; 64 | } 65 | 66 | /// In most case the format isn't important because the users won't see the raw data directly anyway. 67 | /// Thus we provide a default format for the macro. 68 | pub struct Cbor; 69 | 70 | impl SerdeFormat for Cbor { 71 | fn to_vec(s: &S) -> Result, ()> { 72 | serde_cbor::to_vec(s).map_err(|_| ()) 73 | } 74 | 75 | fn from_slice(data: &[u8]) -> Result { 76 | serde_cbor::from_slice(data).map_err(|_| ()) 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /remote-trait-object/src/service/export_import.rs: -------------------------------------------------------------------------------- 1 | use super::Dispatch; 2 | use super::*; 3 | use serde::{Deserialize, Serialize}; 4 | use std::sync::Arc; 5 | 6 | /// An identifier of the skeleton. 7 | /// 8 | /// This represents an identifier of a skeleton, and can create a proxy object using [`import_service_from_handle()`] 9 | /// 10 | /// Note that you will never need this if you do only plain export & import using [`ServiceRef`], [`ServiceToExport`], or [`ServiceToImport`]. 11 | /// See the [module-level documentation] to understand when to use this. 12 | /// 13 | /// [`import_service_from_handle()`]: ../raw_exchange/fn.import_service_from_handle.html 14 | /// [`ServiceToExport`]: ../struct.ServiceToExport.html 15 | /// [`ServiceToImport`]: ../struct.ServiceToImport.html 16 | /// [`ServiceRef`]: ../enum.ServiceRef.html 17 | /// [module-level documentation]: ../raw_exchange/index.html 18 | #[derive(PartialEq, Serialize, Deserialize, Debug, Clone, Copy)] 19 | pub struct HandleToExchange(pub(crate) ServiceObjectId); 20 | 21 | impl HandleToExchange { 22 | /// Creates a null handle. 23 | /// 24 | /// Any proxy object made from this will always panic for all methods. 25 | /// If such proxy object is dropped, it won't send any delete request, so never fails. 26 | /// 27 | /// It is useful when you have a proxy object which has to be initialized later. 28 | pub fn create_null() -> Self { 29 | Self(crate::forwarder::NULL_ID) 30 | } 31 | } 32 | 33 | /// An opaque service to register in the context. 34 | /// 35 | /// See the general description of the concept _skeleton_ [here](https://github.com/CodeChain-io/remote-trait-object) 36 | /// and the definition in this crate [here](https://github.com/CodeChain-io/remote-trait-object). 37 | /// 38 | /// It is constructed with a service object with whichever smart pointer you want. 39 | /// Depending on use of `&mut self` in the methods in the service trait, some or all `Box<>`, `Arc<>`, `Arc>` will implement 40 | /// [`IntoSkeleton`] automatically by the proc macro. 41 | /// Please see [this section](https://github.com/CodeChain-io/remote-trait-object) for more detail about smart pointers. 42 | /// 43 | /// `Skeleton` is useful when you want to erase the trait, and hold it as an opaque service that will be registered later. 44 | /// 45 | /// Note that you will never need this if you do only plain export & import using [`ServiceRef`], [`ServiceToExport`], or [`ServiceToImport`]. 46 | /// See the [module-level documentation] to understand when to use this. 47 | /// 48 | /// [`IntoSkeleton`]: trait.IntoSkeleton.html 49 | /// [`ServiceToExport`]: ../struct.ServiceToExport.html 50 | /// [`ServiceToImport`]: ../struct.ServiceToImport.html 51 | /// [`ServiceRef`]: ../enum.ServiceRef.html 52 | /// [module-level documentation]: ../raw_exchange/index.html 53 | pub struct Skeleton { 54 | pub(crate) raw: Arc, 55 | } 56 | 57 | impl Clone for Skeleton { 58 | /// Clones a `Skeleton`, while sharing the actual single service object. 59 | /// 60 | /// This is useful when you want to export a single service object to multiple connections. 61 | fn clone(&self) -> Self { 62 | Self { 63 | raw: Arc::clone(&self.raw), 64 | } 65 | } 66 | } 67 | 68 | impl Skeleton { 69 | pub fn new(service: impl IntoSkeleton) -> Self { 70 | service.into_skeleton() 71 | } 72 | } 73 | 74 | impl std::fmt::Debug for Skeleton { 75 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 76 | f.write_str("remote-trait-object skeleton") 77 | } 78 | } 79 | 80 | // This belongs to macro_env 81 | pub fn create_skeleton(raw: Arc) -> Skeleton { 82 | Skeleton { raw } 83 | } 84 | 85 | // This belongs to macro_env 86 | pub fn get_dispatch(skeleton: &Skeleton) -> &dyn Dispatch { 87 | skeleton.raw.as_ref() 88 | } 89 | 90 | // These traits are associated with some specific service trait. 91 | // These tratis will be implement by `dyn ServiceTrait` where `T = dyn ServiceTrait` as well. 92 | // Macro will implement this trait with the target(expanding) service trait. 93 | 94 | /// Conversion into a [`Skeleton`], from a smart pointer of a service object. 95 | /// 96 | /// By attaching `[remote_trait_object::service]` on a trait, smart pointers of the trait will automatically implement this. 97 | /// This is required if you want to create a [`Skeleton`] or [`ServiceToExport`]. 98 | /// 99 | /// [`ServiceToExport`]: ../struct.ServiceToExport.html 100 | // Unused T is for avoiding violation of the orphan rule 101 | // T will be local type for the crate, and that makes it possible to 102 | // impl IntoSkeleton for Arc 103 | pub trait IntoSkeleton { 104 | fn into_skeleton(self) -> Skeleton; 105 | } 106 | 107 | /// Conversion into a smart pointer of a service object, from [`HandleToExchange`]. 108 | /// 109 | /// By attaching `[remote_trait_object::service]` on a trait, smart pointers of the trait will automatically implement this. 110 | /// This is required if you want to create a proxy object from [`HandleToExchange`] or [`ServiceToImport`]. 111 | /// 112 | /// [`ServiceToImport`]: ../struct.ServiceToImport.html 113 | // Unused T is for avoiding violation of the orphan rule, like `IntoSkeleton` 114 | pub trait ImportProxy: Sized { 115 | fn import_proxy(port: Weak, handle: HandleToExchange) -> Self; 116 | } 117 | 118 | #[doc(hidden)] 119 | pub trait FromSkeleton: Sized { 120 | fn from_skeleton(skeleton: Skeleton) -> Self; 121 | } 122 | 123 | /// Exports a skeleton and returns a handle to it. 124 | /// 125 | /// Once you create an instance of skeleton, you will eventually export it calling this. 126 | /// Take the handle to the other side's context and call [`import_service_from_handle`] to import it into a proxy object. 127 | /// If not, the service object will remain in the Context forever doing nothing. 128 | pub fn export_service_into_handle( 129 | context: &crate::context::Context, 130 | service: Skeleton, 131 | ) -> HandleToExchange { 132 | context 133 | .get_port() 134 | .upgrade() 135 | .unwrap() 136 | .register_service(service.raw) 137 | } 138 | 139 | /// Imports a handle into a proxy object. 140 | /// 141 | /// Once you receive an instance of [`HandleToExchange`], you will eventually import it calling this. 142 | /// Such handles must be from the other side's context. 143 | pub fn import_service_from_handle>( 144 | context: &crate::context::Context, 145 | handle: HandleToExchange, 146 | ) -> P { 147 | P::import_proxy(context.get_port(), handle) 148 | } 149 | 150 | /// Create a proxy object that always panic for all methods. 151 | /// 152 | /// This is same as using [`create_null()`] and [`import_service_from_handle()`] but you don't even have to specify the [`Context`] here. 153 | /// 154 | /// [`create_null()`]: ./struct.HandleToExchange.html#method.create_null 155 | /// [`import_service_from_handle()`]: ./fn.import_service_from_handle.html 156 | /// [`Context`]: ../struct.Context.html 157 | pub fn import_null_proxy>() -> P { 158 | P::import_proxy( 159 | crate::port::null_weak_port(), 160 | HandleToExchange::create_null(), 161 | ) 162 | } 163 | -------------------------------------------------------------------------------- /remote-trait-object/src/service/handle.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use crate::forwarder::NULL_ID; 3 | use crate::packet::Packet; 4 | use crate::raw_exchange::HandleToExchange; 5 | use crate::service::{MethodId, SerdeFormat}; 6 | 7 | /// Proxy service will carry this. 8 | #[derive(Debug)] 9 | pub struct Handle { 10 | pub id: ServiceObjectId, 11 | pub port: Weak, 12 | } 13 | 14 | impl Handle { 15 | pub fn new(imported_id: HandleToExchange, port: Weak) -> Self { 16 | Handle { 17 | id: imported_id.0, 18 | port, 19 | } 20 | } 21 | } 22 | 23 | impl Handle { 24 | /// This method is the core of Handle, which serves as a "call stub" for the service trait's method. 25 | /// It carries out user's remote call in a generic way. 26 | /// Invoking this method is role of the macro, by putting appropriate instantiation of this generic 27 | /// for each service trait's method, according to the method signature of each. 28 | pub fn call( 29 | &self, 30 | method: MethodId, 31 | args: &S, 32 | ) -> D { 33 | assert_ne!( 34 | self.id, NULL_ID, 35 | "You invoked a method of a null proxy object." 36 | ); 37 | 38 | super::serde_support::port_thread_local::set_port(self.port.clone()); 39 | let args = F::to_vec(args).unwrap(); 40 | let packet = Packet::new_request(self.id, method, &args); 41 | let response = self.port.upgrade().unwrap().call(packet.view()); 42 | let result = F::from_slice(response.data()).unwrap(); 43 | super::serde_support::port_thread_local::remove_port(); 44 | result 45 | } 46 | } 47 | 48 | impl Drop for Handle { 49 | /// Dropping handle will be signaled to the exporter (_delete request_), so that it can remove the service object as well. 50 | fn drop(&mut self) { 51 | if self.id != NULL_ID { 52 | self.port 53 | .upgrade() 54 | .expect("You must drop the proxy object before the RTO context is dropped") 55 | .delete_request(self.id); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /remote-trait-object/src/service/id.rs: -------------------------------------------------------------------------------- 1 | use super::MethodId; 2 | use linkme::distributed_slice; 3 | use serde::{Deserialize, Serialize}; 4 | use std::collections::{HashMap, HashSet}; 5 | 6 | pub const ID_ORDERING: std::sync::atomic::Ordering = std::sync::atomic::Ordering::SeqCst; 7 | pub type MethodIdAtomic = std::sync::atomic::AtomicU32; 8 | 9 | // linkme crate smartly collects all the registrations generated by the proc-macro 10 | // into a sinlge array in the link time. 11 | // Note that too long linkme-related variable name would cause serious compiler error in MacOS 12 | // So we deliberately make it have a short name 13 | 14 | // Id of methods in services. 15 | // Note that here the two strings mean (trait name, method name) 16 | // Also you can skip calling this, then the method id will be set up for default value 17 | // decided by the order of declaration. 18 | type MethodIdentifierSetter = fn(id: MethodId); 19 | #[distributed_slice] 20 | pub static MID_REG: [(&'static str, &'static str, MethodIdentifierSetter)] = [..]; 21 | 22 | /// This will be provided by the user who cares the compatability between already-compiled service traits. 23 | #[derive(PartialEq, Serialize, Deserialize, Debug, Clone)] 24 | pub struct IdMap { 25 | // This is system-wide; All module will get same ones 26 | pub method_map: Option>, 27 | } 28 | 29 | /// A special function that sets static & global identifiers for the methods. 30 | /// 31 | /// It will be explained in more detail in the next version :) 32 | /// 33 | /// This is supposed to be called only once during the entire lifetime of the process. 34 | /// However it is ok to call multiple times if the IdMap is identical, especially in the 35 | /// tests where each test share that static id list 36 | /// # Examples 37 | /// ``` 38 | /// use remote_trait_object::macro_env::*; 39 | /// #[allow(non_upper_case_globals)] 40 | /// static ID_METHOD_MyTrait_mymethod: MethodIdAtomic = MethodIdAtomic::new(1); 41 | /// #[linkme::distributed_slice(MID_REG)] 42 | /// #[allow(non_upper_case_globals)] 43 | /// static ID_METHOD_ENTRY_MyTrait_mymethod: (&'static str, &'static str, fn(id: MethodId)) = 44 | /// ("MyTrait", "mymethod", id_method_setter_MyTrait_mymethod); 45 | /// #[allow(non_snake_case)] 46 | /// fn id_method_setter_MyTrait_mymethod(id: MethodId) { 47 | /// ID_METHOD_MyTrait_mymethod.store(id, ID_ORDERING); 48 | /// } 49 | /// #[test] 50 | /// fn setup() { 51 | /// let id_map: HashMap<(String, String), MethodId> = 52 | /// [(("MyTrait".to_owned(), "mymethod".to_owned()), 123)].iter().cloned().collect(); 53 | /// let id_map = IdMap { 54 | /// method_map: Some(id_map), 55 | /// }; 56 | /// setup_identifiers(&id_map); 57 | /// assert_eq!(ID_METHOD_MyTrait_mymethod.load(ID_ORDERING), 123); 58 | /// } 59 | /// ``` 60 | pub fn setup_identifiers(descriptor: &IdMap) { 61 | // distributed_slices integrity test 62 | { 63 | let mut bucket: HashSet<(String, String)> = HashSet::new(); 64 | for (ident1, ident2, _) in MID_REG { 65 | bucket.insert(((*ident1).to_owned(), (*ident2).to_owned())); 66 | } 67 | assert_eq!( 68 | bucket.len(), 69 | MID_REG.len(), 70 | "The service traits that this binary involved are not named; 71 | You have provided multiple traits with an identical name" 72 | ); 73 | } 74 | 75 | // method ids have default values decided by the order, so it is ok to leave them in an ordinary case. 76 | if let Some(map) = descriptor.method_map.as_ref() { 77 | for (trait_name, method_name, setter) in MID_REG { 78 | setter( 79 | *map.get(&((*trait_name).to_owned(), (*method_name).to_owned())) 80 | .expect("Invalid handle descriptor"), 81 | ); 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /remote-trait-object/src/service/null.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | 3 | /// NullServive is the only actual service trait that remote-trait-object provides by default. 4 | /// It will be useful when you want to establish a remote-trait-object connection with with_initial_service(), 5 | /// but such initial service is needed by only one side. 6 | pub trait NullService: Service {} 7 | 8 | pub fn create_null_service() -> Box { 9 | Box::new(NullServiceImpl) 10 | } 11 | 12 | struct NullServiceImpl; 13 | 14 | impl NullService for NullServiceImpl {} 15 | 16 | impl Service for NullServiceImpl {} 17 | 18 | // Contents below are something that would have been generated by macro. 19 | // They are slightly different from the actual expansion result of NullService (which happens to succeed), since the macro 20 | // doesn't take account of such special case. 21 | 22 | pub struct NullServiceBoxDispatcher {} 23 | impl NullServiceBoxDispatcher { 24 | fn new(_object: Box) -> Self { 25 | Self {} 26 | } 27 | } 28 | impl crate::macro_env::Dispatch for NullServiceBoxDispatcher { 29 | fn dispatch_and_call(&self, _method: crate::macro_env::MethodId, _args: &[u8]) -> Vec { 30 | panic!("Invalid remote-trait-object call. Fatal Error.") 31 | } 32 | } 33 | impl crate::macro_env::IntoSkeleton for Box { 34 | fn into_skeleton(self) -> crate::macro_env::Skeleton { 35 | crate::macro_env::create_skeleton(std::sync::Arc::new(NullServiceBoxDispatcher::new(self))) 36 | } 37 | } 38 | pub struct NullServiceArcDispatcher {} 39 | impl NullServiceArcDispatcher { 40 | fn new(_object: std::sync::Arc) -> Self { 41 | Self {} 42 | } 43 | } 44 | impl crate::macro_env::Dispatch for NullServiceArcDispatcher { 45 | fn dispatch_and_call(&self, _method: crate::macro_env::MethodId, _args: &[u8]) -> Vec { 46 | panic!("Invalid remote-trait-object call. Fatal Error.") 47 | } 48 | } 49 | impl crate::macro_env::IntoSkeleton for std::sync::Arc { 50 | fn into_skeleton(self) -> crate::macro_env::Skeleton { 51 | crate::macro_env::create_skeleton(std::sync::Arc::new(NullServiceArcDispatcher::new(self))) 52 | } 53 | } 54 | pub struct NullServiceRwLockDispatcher {} 55 | impl NullServiceRwLockDispatcher { 56 | fn new(_object: std::sync::Arc>) -> Self { 57 | Self {} 58 | } 59 | } 60 | impl crate::macro_env::Dispatch for NullServiceRwLockDispatcher { 61 | fn dispatch_and_call(&self, _method: crate::macro_env::MethodId, _args: &[u8]) -> Vec { 62 | panic!("Invalid remote-trait-object call. Fatal Error.") 63 | } 64 | } 65 | impl crate::macro_env::IntoSkeleton 66 | for std::sync::Arc> 67 | { 68 | fn into_skeleton(self) -> crate::macro_env::Skeleton { 69 | crate::macro_env::create_skeleton(std::sync::Arc::new(NullServiceRwLockDispatcher::new( 70 | self, 71 | ))) 72 | } 73 | } 74 | #[derive(Debug)] 75 | pub struct NullServiceProxy { 76 | handle: crate::macro_env::Handle, 77 | } 78 | impl NullService for NullServiceProxy {} 79 | impl crate::macro_env::Service for NullServiceProxy {} 80 | impl crate::macro_env::ImportProxy for Box { 81 | fn import_proxy( 82 | port: std::sync::Weak, 83 | handle: crate::macro_env::HandleToExchange, 84 | ) -> Self { 85 | Box::new(NullServiceProxy { 86 | handle: crate::macro_env::Handle::new(handle, port), 87 | }) 88 | } 89 | } 90 | impl crate::macro_env::ImportProxy for std::sync::Arc { 91 | fn import_proxy( 92 | port: std::sync::Weak, 93 | handle: crate::macro_env::HandleToExchange, 94 | ) -> Self { 95 | std::sync::Arc::new(NullServiceProxy { 96 | handle: crate::macro_env::Handle::new(handle, port), 97 | }) 98 | } 99 | } 100 | impl crate::macro_env::ImportProxy 101 | for std::sync::Arc> 102 | { 103 | fn import_proxy( 104 | port: std::sync::Weak, 105 | handle: crate::macro_env::HandleToExchange, 106 | ) -> Self { 107 | std::sync::Arc::new(parking_lot::RwLock::new(NullServiceProxy { 108 | handle: crate::macro_env::Handle::new(handle, port), 109 | })) 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /remote-trait-object/src/service/serde_support.rs: -------------------------------------------------------------------------------- 1 | use super::export_import::*; 2 | use super::*; 3 | use crate::raw_exchange::HandleToExchange; 4 | use serde::de::{Deserialize, Deserializer}; 5 | use serde::ser::{Serialize, Serializer}; 6 | use std::cell::RefCell; 7 | use std::marker::PhantomData; 8 | use std::sync::Arc; 9 | 10 | /// Some data format traverses over data **twice**, first for size estimation and second for real encoding. 11 | /// Thus we use prepare secondary phase `Exported`. 12 | enum ExportEntry { 13 | ReadyToExport(Skeleton), 14 | Exported(HandleToExchange), 15 | } 16 | 17 | /// A special wrapper of _skeleton_, used to export a service object. 18 | /// 19 | /// You can make any smart pointer of a trait object into a `ServiceToExport` 20 | /// as far as the trait is a subtrait of [`Service`] and marked with the macro [`service`]. 21 | /// 22 | /// The only way to actually export a `ServiceToExport` is either 23 | /// 1. Returning it while handling a method in another service 24 | /// 2. Passing as an argument in a remote call 25 | /// 26 | /// `ServiceToExport` is a compatible type with [`ServiceToImport`] and [`ServiceRef`] 27 | /// You can import a proxy object by putting one of those two types on the same place in the client side. 28 | /// See [Service Compatibility] section 29 | /// 30 | /// `ServiceToExport` is a wrapper of [`Skeleton`] that hides the detail exchange process. 31 | /// However you don't have to know what [`Skeleton`] is, unless you're going to perform [raw export and import]. 32 | /// 33 | /// **NOTE**: it implements [`Serialize`], but you must **NEVER** try to serialize it. 34 | /// It has a side effect of registering the service object in the context, 35 | /// and so should be called only by `remote-trait-object`'s internal process. 36 | /** 37 | ## Example 38 | ``` 39 | use remote_trait_object::*; 40 | use std::sync::Arc; 41 | use parking_lot::RwLock; 42 | 43 | #[service(no_proxy)] 44 | pub trait Hello: Service { 45 | fn hello(&self) -> Vec>; 46 | } 47 | 48 | struct A; 49 | impl Service for A {} 50 | impl Hello for A { 51 | fn hello(&self) -> Vec> { 52 | // Export by return 53 | vec![ 54 | ServiceToExport::new(Box::new(A) as Box), 55 | ServiceToExport::new(Arc::new(A) as Arc), 56 | ServiceToExport::new(Arc::new(RwLock::new(A)) as Arc>), 57 | ] 58 | } 59 | } 60 | ``` 61 | **/ 62 | /// [`service`]: attr.service.html 63 | /// [Service compatibility]: ./index.html#service_compatibility 64 | /// [raw export and import]: raw_exchange/index.html 65 | pub struct ServiceToExport { 66 | service: RefCell, 67 | _marker: PhantomData, 68 | } 69 | 70 | impl ServiceToExport { 71 | /// Creates a new instance from a smart pointer of a service object. 72 | pub fn new(service: impl IntoSkeleton) -> Self { 73 | Self { 74 | service: RefCell::new(ExportEntry::ReadyToExport(service.into_skeleton())), 75 | _marker: PhantomData, 76 | } 77 | } 78 | 79 | pub(crate) fn get_raw_export(self) -> Skeleton { 80 | match self.service.into_inner() { 81 | ExportEntry::ReadyToExport(s) => s, 82 | _ => panic!(), 83 | } 84 | } 85 | } 86 | 87 | /// A special wrapper of _handle_, used to import a service. 88 | /// 89 | /// You can make an instance of this into any smart pointer of a trait object 90 | /// as far as the trait is a subtrait of [`Service`] and marked with the macro [`service`]. 91 | /// 92 | /// The only way to actually import a `ServiceToImport` is either 93 | /// 1. Receiving it as a return value, in a remote call 94 | /// 2. Receiving as an argument while a method in handling another service 95 | /// 96 | /// `ServiceToImport` is a compatible type with [`ServiceToExport`] and [`ServiceRef`] 97 | /// You can export a service object by putting one of those two types on the same place on the server side. 98 | /// See [Service Compatibility] section for more. 99 | /// 100 | /// `ServiceToImport` is a wrapper of [`HandleToExchange`] that hides the detail exchange process. 101 | /// However you don't have to know what [`HandleToExchange`] is, unless you're going to perform [raw export and import]. 102 | /// 103 | /// **NOTE**: it implements [`Deserialize`], but you must **NEVER** try to deserialize it. 104 | /// It has a side effect of registering the handle in the context, 105 | /// and so should be called only by `remote-trait-object`'s internal process. 106 | /** 107 | ## Example 108 | ``` 109 | use remote_trait_object::*; 110 | use std::sync::Arc; 111 | use parking_lot::RwLock; 112 | 113 | #[service(no_skeleton)] 114 | pub trait Hello: Service { 115 | fn hello(&self) -> Vec>; 116 | } 117 | 118 | fn do_some_imports(x: Box) { 119 | let mut v = x.hello(); 120 | let a: Box = v.pop().unwrap().into_proxy(); 121 | let b: Arc = v.pop().unwrap().into_proxy(); 122 | let c: Arc> = v.pop().unwrap().into_proxy(); 123 | } 124 | ``` 125 | **/ 126 | /// [`service`]: attr.service.html 127 | /// [Service compatibility]: ./index.html#service_compatibility 128 | /// [raw export and import]: raw_exchange/index.html 129 | pub struct ServiceToImport { 130 | handle: HandleToExchange, 131 | port: Weak, 132 | _marker: PhantomData, 133 | } 134 | 135 | impl ServiceToImport { 136 | /// Converts itself into a smart pointer of the trait, which is a _proxy object_. 137 | pub fn into_proxy>(self) -> P { 138 | P::import_proxy(self.port, self.handle) 139 | } 140 | 141 | /// Casts into another `ServiceToImport` with a different service trait. 142 | /// 143 | /// If the target trait is not compatible with the original one, it returns `Err`. 144 | /// 145 | /// See [Service Compatiblity] section for more. 146 | /// 147 | /// [Service compatiblity]: ./index.html#service_compatibility 148 | #[allow(clippy::result_unit_err)] 149 | pub fn cast_service(self) -> Result, ()> { 150 | // TODO: Check the compatibility between traits using IDL 151 | Ok(ServiceToImport { 152 | handle: self.handle, 153 | port: self.port, 154 | _marker: PhantomData, 155 | }) 156 | } 157 | 158 | /// Casts into another `ServiceToImport` with a different service trait, without check. 159 | /// 160 | /// If the target trait is not compatible with the original one, any method call of the proxy object imported with this 161 | /// will cause a serious error. 162 | /// 163 | /// See [Service Compatiblity] section for more. 164 | /// 165 | /// [Service compatiblity]: ./index.html#service_compatibility 166 | pub fn cast_service_without_compatibility_check( 167 | self, 168 | ) -> ServiceToImport { 169 | ServiceToImport { 170 | handle: self.handle, 171 | port: self.port, 172 | _marker: PhantomData, 173 | } 174 | } 175 | 176 | pub(crate) fn from_raw_import(handle: HandleToExchange, port: Weak) -> Self { 177 | Self { 178 | handle, 179 | port, 180 | _marker: PhantomData, 181 | } 182 | } 183 | } 184 | 185 | /// A union of [`ServiceToExport`] and [`ServiceToImport`] 186 | /// 187 | /// **In most case, you will mostly use only this struct to export and import services.** 188 | /// 189 | /// This is needed when you want to export and import a service via another service, 190 | /// but using a common single trait as a channel. 191 | /// 192 | /// Suppose you want to export `Box` by returning it in another service's method, `fn order_pizza()`. 193 | /// One way of doing this is defining two traits, one for export and one for import. 194 | /// Crate that wants to implement & export a `PizzaStore` and export `Pizza` will have following code 195 | /** 196 | ``` 197 | use remote_trait_object::*; 198 | 199 | #[service] 200 | pub trait Pizza: Service {} 201 | 202 | #[service(no_proxy)] 203 | pub trait PizzaStore: Service { 204 | fn order_pizza(&self) -> ServiceToExport; 205 | } 206 | ``` 207 | 208 | On the other hand, crate that wants to import & remotely call a `PizzaStore` and import `Pizza` will have following code 209 | 210 | ``` 211 | use remote_trait_object::*; 212 | 213 | #[service] 214 | pub trait Pizza: Service {} 215 | 216 | #[service(no_skeleton)] 217 | pub trait PizzaStore: Service { 218 | fn order_pizza(&self) -> ServiceToImport; 219 | } 220 | ``` 221 | **/ 222 | /// This works perfectly fine, by returning `ServiceToExport::new(..)` in the former and 223 | /// calling `ServiceToImport::into_remote(the_return_value)` in the latter, 224 | /// because the two `PizzaStore`s are compatible since [`ServiceToImport`] and [`ServiceToExport`] are compatible. 225 | /// 226 | /// However, suppose the case where the two parties may have a common dependent crate which would have defined such a common service trait, 227 | /// or even the case where the two parties are in the same crate. 228 | /// It becomes somewhat bothersome to have both duplicated traits only because of [`ServiceToExport`] and [`ServiceToImport`]. 229 | /// 230 | /// `ServiceRef` is for such purpose. Instead of denoting both [`ServiceToExport`] and [`ServiceToImport`] for two separate traits, just use 231 | /// a single common trait with those two types replaced with `ServiceRef` in the very same place. 232 | /// 233 | /// The above two traits now become a single trait. 234 | /** 235 | ``` 236 | use remote_trait_object::*; 237 | 238 | #[service] 239 | pub trait Pizza: Service {} 240 | 241 | #[service] 242 | pub trait PizzaStore: Service { 243 | fn order_pizza(&self) -> ServiceRef; 244 | } 245 | ``` 246 | **/ 247 | /// And this `PizzaStore` can be both 248 | /// - implemented and exported - you will be using `Import` variant for an argument, and `Export` variant for the return value. 249 | /// - imported and locally invoked - you will be using `Export` variant for an argument, and `Import` variant for the return value. 250 | /// 251 | /// ## Example 252 | /** 253 | ```ignore 254 | // EXPORTER SIDE 255 | impl PizzaStore for SomeType { 256 | fn order_pizza(&self) -> ServiceRef { 257 | ServiceRef::create_export(Box::new(SomePizza) as Box) 258 | } 259 | } 260 | 261 | // IMPORTER SIDE 262 | let store: Box = some_store(); 263 | let pizza: Box = store.order_pizza().unwrap_import().into_proxy(); 264 | ``` 265 | **/ 266 | pub enum ServiceRef { 267 | Export(ServiceToExport), 268 | Import(ServiceToImport), 269 | } 270 | 271 | impl ServiceRef { 272 | /// Creates an `Export` variant from a smart pointer of a service object. 273 | /// 274 | /// It simply `ServiceRef::Export(ServiceToExport::new(service))`. 275 | pub fn create_export(service: impl IntoSkeleton) -> Self { 276 | ServiceRef::Export(ServiceToExport::new(service)) 277 | } 278 | 279 | /// Unwraps as an `Import` variant. 280 | /// 281 | /// It panics if it is `Export`. 282 | pub fn unwrap_import(self) -> ServiceToImport { 283 | match self { 284 | ServiceRef::Import(x) => x, 285 | _ => panic!("You can import ony imported ServiceRef"), 286 | } 287 | } 288 | 289 | /// Converts into the object with whatever smart pointer type you want, for both variants. 290 | /// 291 | /// If `ServiceRef` is constructed with `Import` (the common case), it is same as `unwrap_import().into_proxy()`. 292 | /// If `ServiceRef` is constructed with `Export` (where the `ServiceRef` is given by local object), 293 | /// it just create a light-weight object that simply wraps the skeleton, not involving any connections. 294 | pub fn into_object + FromSkeleton>(self) -> P { 295 | match self { 296 | ServiceRef::Import(x) => x.into_proxy(), 297 | ServiceRef::Export(x) => P::from_skeleton(x.get_raw_export()), 298 | } 299 | } 300 | } 301 | 302 | /// This manages thread-local pointer of the port, which will be used in serialization of 303 | /// service objects wrapped in the S* pointers. Cuttently it is the only way to deliver the port 304 | /// within the de/serialization context. 305 | /// TODO: check that serde doens't spawn a thread while serializing. 306 | pub(crate) mod port_thread_local { 307 | use super::*; 308 | use std::cell::RefCell; 309 | 310 | // TODO 311 | // If a service call another service, this PORT setting might be stacked (at most twice). 312 | // We check that the consistency of stacking for an assertion purpose. 313 | // However it might be costly considering the frequency of this operations, 314 | // so please replace this with unchecking logic 315 | // after the library becomes stabilized. 316 | thread_local!(static PORT: RefCell>> = RefCell::new(Vec::new())); 317 | 318 | pub fn set_port(port: Weak) { 319 | PORT.with(|k| { 320 | k.try_borrow_mut().unwrap().push(port); 321 | assert!(k.borrow().len() <= 2); 322 | }) 323 | } 324 | 325 | pub fn get_port() -> Weak { 326 | PORT.with(|k| k.borrow().last().unwrap().clone()) 327 | } 328 | 329 | pub fn remove_port() { 330 | PORT.with(|k| { 331 | k.try_borrow_mut().unwrap().pop().unwrap(); 332 | }) 333 | } 334 | } 335 | 336 | impl Serialize for ServiceToExport { 337 | fn serialize(&self, serializer: S) -> Result 338 | where 339 | S: Serializer, 340 | { 341 | let error = "You must not de/serialize ServiceRef by yourself. If you not, this is a bug."; 342 | let (handle, have_to_replace) = match &*self.service.borrow() { 343 | ExportEntry::ReadyToExport(service) => { 344 | debug_assert_eq!(Arc::strong_count(&service.raw), 1); 345 | ( 346 | port_thread_local::get_port() 347 | .upgrade() 348 | .expect(error) 349 | .register_service(Arc::clone(&service.raw)), 350 | true, 351 | ) 352 | } 353 | ExportEntry::Exported(handle) => (*handle, false), 354 | }; 355 | if have_to_replace { 356 | *self.service.borrow_mut() = ExportEntry::Exported(handle) 357 | } 358 | handle.serialize(serializer) 359 | } 360 | } 361 | 362 | impl<'de, T: ?Sized + Service> Deserialize<'de> for ServiceToImport { 363 | fn deserialize(deserializer: D) -> Result 364 | where 365 | D: Deserializer<'de>, 366 | { 367 | let handle = HandleToExchange::deserialize(deserializer)?; 368 | Ok(ServiceToImport { 369 | handle, 370 | port: port_thread_local::get_port(), 371 | _marker: std::marker::PhantomData, 372 | }) 373 | } 374 | } 375 | 376 | impl Serialize for ServiceRef { 377 | fn serialize(&self, serializer: S) -> Result 378 | where 379 | S: Serializer, 380 | { 381 | match self { 382 | ServiceRef::Export(x) => x.serialize(serializer), 383 | ServiceRef::Import(_) => panic!( 384 | "If you want to re-export an imported object, first completely import it with `into_proxy()` and make it into `ServiceToExport`." 385 | ), 386 | } 387 | } 388 | } 389 | 390 | impl<'de, T: ?Sized + Service> Deserialize<'de> for ServiceRef { 391 | fn deserialize(deserializer: D) -> Result 392 | where 393 | D: Deserializer<'de>, 394 | { 395 | Ok(ServiceRef::Import(ServiceToImport::deserialize( 396 | deserializer, 397 | )?)) 398 | } 399 | } 400 | 401 | #[cfg(test)] 402 | mod tests { 403 | mod serialize_test { 404 | use super::super::ServiceRef; 405 | use crate::macro_env::*; 406 | use crate::packet::*; 407 | use crate::port::Port; 408 | use crate::service::ServiceObjectId; 409 | use crate::*; 410 | use std::sync::atomic::{AtomicU32, Ordering}; 411 | use std::sync::{Arc, Weak}; 412 | 413 | #[derive(Debug)] 414 | pub struct MockPort { 415 | count: AtomicU32, 416 | } 417 | 418 | impl Port for MockPort { 419 | fn call(&self, _packet: PacketView) -> Packet { 420 | unimplemented!() 421 | } 422 | 423 | fn register_service(&self, _service_object: Arc) -> HandleToExchange { 424 | self.count.fetch_add(1, Ordering::SeqCst); 425 | HandleToExchange(123) 426 | } 427 | 428 | fn delete_request(&self, _id: ServiceObjectId) { 429 | unimplemented!() 430 | } 431 | } 432 | 433 | trait Foo: Service {} 434 | 435 | struct FooImpl; 436 | impl Foo for FooImpl {} 437 | impl Service for FooImpl {} 438 | impl Dispatch for FooImpl { 439 | fn dispatch_and_call(&self, _method: MethodId, _args: &[u8]) -> Vec { 440 | unimplemented!() 441 | } 442 | } 443 | 444 | impl IntoSkeleton for Arc { 445 | fn into_skeleton(self) -> crate::macro_env::Skeleton { 446 | crate::macro_env::create_skeleton(Arc::new(FooImpl)) 447 | } 448 | } 449 | 450 | /// This test checks SArc is serialized as HandleToExchange or not 451 | #[test] 452 | fn test_serialize() { 453 | let port = Arc::new(MockPort { 454 | count: AtomicU32::new(0), 455 | }); 456 | let weak_port = Arc::downgrade(&port) as Weak; 457 | super::super::port_thread_local::set_port(weak_port); 458 | 459 | { 460 | let foo_arc: Arc = Arc::new(FooImpl); 461 | let foo_sarc = ServiceRef::create_export(foo_arc.clone()); 462 | let bytes = serde_json::to_vec(&foo_sarc).unwrap(); 463 | let handle_to_exchange: HandleToExchange = serde_json::from_slice(&bytes).unwrap(); 464 | assert_eq!(handle_to_exchange.0, 123); 465 | assert_eq!(port.count.load(Ordering::SeqCst), 1); 466 | } 467 | 468 | { 469 | let foo_arc: Arc = Arc::new(FooImpl); 470 | let foo_sarc = ServiceRef::create_export(foo_arc.clone()); 471 | let bytes = serde_cbor::to_vec(&foo_sarc).unwrap(); 472 | let handle_to_exchange: HandleToExchange = serde_cbor::from_slice(&bytes).unwrap(); 473 | assert_eq!(handle_to_exchange.0, 123); 474 | assert_eq!(port.count.load(Ordering::SeqCst), 2); 475 | } 476 | 477 | { 478 | let foo_arc: Arc = Arc::new(FooImpl); 479 | let foo_sarc = ServiceRef::create_export(foo_arc.clone()); 480 | let bytes = bincode::serialize(&foo_sarc).unwrap(); 481 | let handle_to_exchange: HandleToExchange = bincode::deserialize(&bytes).unwrap(); 482 | assert_eq!(handle_to_exchange.0, 123); 483 | assert_eq!(port.count.load(Ordering::SeqCst), 3); 484 | } 485 | } 486 | } 487 | 488 | mod deserialize_test { 489 | use super::super::ServiceRef; 490 | use crate::port::Port; 491 | use crate::{raw_exchange::*, Service}; 492 | use std::sync::Weak; 493 | 494 | trait Foo: Service { 495 | fn get_handle_to_exchange(&self) -> HandleToExchange; 496 | } 497 | struct FooImpl { 498 | handle_to_exchange: HandleToExchange, 499 | } 500 | impl Foo for FooImpl { 501 | fn get_handle_to_exchange(&self) -> HandleToExchange { 502 | self.handle_to_exchange 503 | } 504 | } 505 | impl Service for FooImpl {} 506 | impl ImportProxy for Box { 507 | fn import_proxy(_port: Weak, handle: HandleToExchange) -> Box { 508 | Box::new(FooImpl { 509 | handle_to_exchange: handle, 510 | }) 511 | } 512 | } 513 | 514 | #[test] 515 | fn test_deserialize() { 516 | super::super::port_thread_local::set_port(crate::port::null_weak_port()); 517 | 518 | { 519 | let handle_to_exchange = HandleToExchange(32); 520 | let serialized_handle = serde_cbor::to_vec(&handle_to_exchange).unwrap(); 521 | let dyn_foo: ServiceRef = 522 | serde_cbor::from_slice(&serialized_handle).unwrap(); 523 | assert_eq!( 524 | dyn_foo 525 | .unwrap_import() 526 | .into_proxy::>() 527 | .get_handle_to_exchange() 528 | .0, 529 | 32 530 | ); 531 | } 532 | 533 | { 534 | let handle_to_exchange = HandleToExchange(2); 535 | let serialized_handle = serde_cbor::to_vec(&handle_to_exchange).unwrap(); 536 | let dyn_foo: ServiceRef = 537 | serde_cbor::from_slice(&serialized_handle).unwrap(); 538 | assert_eq!( 539 | dyn_foo 540 | .unwrap_import() 541 | .into_proxy::>() 542 | .get_handle_to_exchange() 543 | .0, 544 | 2 545 | ); 546 | } 547 | } 548 | } 549 | } 550 | -------------------------------------------------------------------------------- /remote-trait-object/src/tests.rs: -------------------------------------------------------------------------------- 1 | mod complex_trait; 2 | 3 | use crate as remote_trait_object; 4 | use remote_trait_object_macro as rto_macro; 5 | 6 | use crate::forwarder::ServiceObjectId; 7 | use crate::packet::{Packet, PacketView}; 8 | use crate::port::*; 9 | use crate::raw_exchange::{HandleToExchange, ImportProxy, IntoSkeleton}; 10 | use crate::service::*; 11 | use parking_lot::Mutex; 12 | use std::collections::HashMap; 13 | use std::sync::Arc; 14 | 15 | struct TestDispatchMap { 16 | last_id: u32, 17 | map: HashMap>, 18 | } 19 | 20 | impl TestDispatchMap { 21 | pub fn new() -> Self { 22 | Self { 23 | last_id: 0, 24 | map: HashMap::new(), 25 | } 26 | } 27 | 28 | fn insert(&mut self, service_object: Arc) -> u32 { 29 | self.last_id += 1; 30 | self.map.insert(self.last_id, service_object); 31 | self.last_id 32 | } 33 | 34 | fn get_cloned(&mut self, id: u32) -> Arc { 35 | Arc::clone(&self.map.get(&id).unwrap()) 36 | } 37 | 38 | fn remove(&mut self, id: u32) { 39 | self.map.remove(&id); 40 | } 41 | 42 | fn len(&self) -> usize { 43 | self.map.len() 44 | } 45 | } 46 | 47 | pub(crate) struct TestPort { 48 | dispatch_map: Mutex, 49 | } 50 | 51 | impl TestPort { 52 | pub fn new() -> Self { 53 | Self { 54 | dispatch_map: Mutex::new(TestDispatchMap::new()), 55 | } 56 | } 57 | 58 | fn register_len(&self) -> usize { 59 | self.dispatch_map.lock().len() 60 | } 61 | } 62 | 63 | impl std::fmt::Debug for TestPort { 64 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 65 | write!(f, "") 66 | } 67 | } 68 | 69 | impl Port for TestPort { 70 | fn call(&self, packet: PacketView) -> Packet { 71 | let object_id = packet.object_id(); 72 | let dispatcher = self.dispatch_map.lock().get_cloned(object_id); 73 | let response = dispatcher.dispatch_and_call(packet.method(), packet.data()); 74 | let mut response_packet = Packet::new_response_from_request(packet); 75 | response_packet.append_data(&response); 76 | response_packet 77 | } 78 | 79 | fn delete_request(&self, id: ServiceObjectId) { 80 | self.dispatch_map.lock().remove(id); 81 | } 82 | 83 | fn register_service(&self, service_object: Arc) -> HandleToExchange { 84 | HandleToExchange(self.dispatch_map.lock().insert(service_object)) 85 | } 86 | } 87 | 88 | #[rto_macro::service] 89 | pub trait Service1: Service { 90 | fn f1(&self, a1: i32, a2: &i32, a3: &[i32], a4: (i32, i32), a5: &(i32, String)) -> i32; 91 | fn f2(&self, s1: &str, a2: &Option) -> (String, String); 92 | } 93 | 94 | struct MyObject { 95 | mul: i32, 96 | } 97 | 98 | impl Service for MyObject {} 99 | 100 | impl Service1 for MyObject { 101 | fn f1(&self, a1: i32, a2: &i32, a3: &[i32], a4: (i32, i32), a5: &(i32, String)) -> i32 { 102 | let sum: i32 = a3.iter().sum(); 103 | (a1 + *a2 + sum + a4.0 + a4.1 + a5.0 + a5.1.parse::().unwrap()) * self.mul 104 | } 105 | 106 | fn f2(&self, s1: &str, a2: &Option) -> (String, String) { 107 | if let Some(x) = a2.as_ref() { 108 | (format!("{}_{}_{}", s1, x, self.mul), "Bye".to_owned()) 109 | } else { 110 | ( 111 | format!("{}_{}_{}", s1, "None", self.mul), 112 | "ByeBye".to_owned(), 113 | ) 114 | } 115 | } 116 | } 117 | 118 | #[test] 119 | fn macro1() { 120 | let port = Arc::new(TestPort::new()); 121 | let port_weak = Arc::downgrade(&port); 122 | 123 | let object = Box::new(MyObject { mul: 4 }) as Box; 124 | let handle = port.register_service(object.into_skeleton().raw); 125 | let proxy = as ImportProxy>::import_proxy(port_weak, handle); 126 | 127 | assert_eq!( 128 | proxy.f1(1, &2, &[3, 4], (5, 6), &(7, "8".to_owned())), 129 | (1 + 2 + 3 + 4 + 5 + 6 + 7 + 8) * 4 130 | ); 131 | assert_eq!( 132 | proxy.f2("Hello", &Some(123)), 133 | ("Hello_123_4".to_owned(), "Bye".to_owned()) 134 | ); 135 | assert_eq!( 136 | proxy.f2("Hello", &None), 137 | ("Hello_None_4".to_owned(), "ByeBye".to_owned()) 138 | ); 139 | drop(proxy); 140 | assert_eq!(port.register_len(), 0); 141 | } 142 | 143 | #[rto_macro::service] 144 | trait Hello: Service { 145 | fn f(&self, v: &[(i32, i32)]) -> i32; 146 | } 147 | 148 | struct SimpleHello; 149 | impl Service for SimpleHello {} 150 | 151 | impl Hello for SimpleHello { 152 | fn f(&self, v: &[(i32, i32)]) -> i32 { 153 | v.iter().map(|(x, y)| x + y).sum() 154 | } 155 | } 156 | 157 | /// This trait causes a compile error without `no_skeleton` 158 | #[rto_macro::service(no_skeleton)] 159 | trait HelloWithRef: Service { 160 | fn f(&self, v: &[&(&i32, &i32)]) -> i32; 161 | } 162 | 163 | #[test] 164 | fn macro_no_skeleton() { 165 | let port = Arc::new(TestPort::new()); 166 | let port_weak = Arc::downgrade(&port); 167 | 168 | let object = Box::new(SimpleHello) as Box; 169 | 170 | let handle = port.register_service(object.into_skeleton().raw); 171 | let proxy = 172 | as ImportProxy>::import_proxy(port_weak, handle); 173 | 174 | let source = vec![1, 2, 3, 4]; 175 | let source2 = vec![(&source[0], &source[1]), (&source[2], &source[3])]; 176 | let source3 = vec![&source2[0], &source2[1]]; 177 | 178 | assert_eq!(proxy.f(&source3), 10); 179 | drop(proxy); 180 | assert_eq!(port.register_len(), 0); 181 | } 182 | -------------------------------------------------------------------------------- /remote-trait-object/src/tests/complex_trait.rs: -------------------------------------------------------------------------------- 1 | use super::TestPort; 2 | use crate as remote_trait_object; 3 | use crate::port::Port; 4 | use crate::raw_exchange::*; 5 | use crate::{Service, ServiceRef, ServiceToExport}; 6 | use remote_trait_object_macro as rto_macro; 7 | use std::sync::{Arc, Mutex}; 8 | 9 | #[rto_macro::service] 10 | trait A: Service { 11 | fn service_object_as_argument(&self, b: ServiceRef); 12 | fn service_object_as_return(&self) -> ServiceRef; 13 | fn recursive_service_object(&self) -> ServiceRef; 14 | fn get_recursion_count(&self) -> u32; 15 | } 16 | 17 | #[rto_macro::service] 18 | trait B: Service { 19 | fn inc(&self); 20 | fn get(&self) -> i32; 21 | } 22 | 23 | struct SimpleA { 24 | recursion_count: u32, 25 | } 26 | 27 | impl SimpleA { 28 | pub fn new() -> Self { 29 | Self { recursion_count: 0 } 30 | } 31 | 32 | pub fn with_recursion_count(recursion_count: u32) -> Self { 33 | Self { recursion_count } 34 | } 35 | } 36 | 37 | impl A for SimpleA { 38 | fn service_object_as_argument(&self, b: ServiceRef) { 39 | let b: Box = b.unwrap_import().into_proxy(); 40 | assert_eq!(0, b.get()); 41 | b.inc(); 42 | b.inc(); 43 | b.inc(); 44 | assert_eq!(3, b.get()); 45 | } 46 | 47 | fn service_object_as_return(&self) -> ServiceRef { 48 | let b = Box::new(SimpleB::new()) as Box; 49 | ServiceRef::Export(ServiceToExport::new(b)) 50 | } 51 | 52 | fn recursive_service_object(&self) -> ServiceRef { 53 | let a = Box::new(SimpleA::with_recursion_count(self.recursion_count + 1)) as Box; 54 | ServiceRef::Export(ServiceToExport::new(a)) 55 | } 56 | 57 | fn get_recursion_count(&self) -> u32 { 58 | self.recursion_count 59 | } 60 | } 61 | 62 | impl Service for SimpleA {} 63 | 64 | struct SimpleB { 65 | count: Mutex, 66 | } 67 | 68 | impl SimpleB { 69 | pub fn new() -> Self { 70 | Self { 71 | count: Mutex::new(0), 72 | } 73 | } 74 | } 75 | 76 | impl Service for SimpleB {} 77 | impl B for SimpleB { 78 | fn inc(&self) { 79 | *self.count.lock().unwrap() += 1 80 | } 81 | fn get(&self) -> i32 { 82 | *self.count.lock().unwrap() 83 | } 84 | } 85 | 86 | fn init_logger() { 87 | let _ = env_logger::builder().is_test(true).try_init(); 88 | } 89 | 90 | fn create_proxy_a(port: Arc) -> Arc { 91 | let a: Arc = Arc::new(SimpleA::new()); 92 | let handle = port.register_service(a.into_skeleton().raw); 93 | ImportProxy::import_proxy(Arc::downgrade(&port), handle) 94 | } 95 | 96 | #[test] 97 | fn service_object_as_return() { 98 | init_logger(); 99 | 100 | let port = Arc::new(TestPort::new()); 101 | let proxy_a = create_proxy_a(port.clone()); 102 | 103 | let proxy_b: Box = proxy_a 104 | .service_object_as_return() 105 | .unwrap_import() 106 | .into_proxy(); 107 | assert_eq!(proxy_b.get(), 0); 108 | proxy_b.inc(); 109 | assert_eq!(proxy_b.get(), 1); 110 | proxy_b.inc(); 111 | assert_eq!(proxy_b.get(), 2); 112 | 113 | drop(proxy_a); 114 | drop(proxy_b); 115 | drop(port) 116 | } 117 | 118 | #[test] 119 | fn service_object_as_argument() { 120 | init_logger(); 121 | 122 | let port = Arc::new(TestPort::new()); 123 | let proxy_a = create_proxy_a(port.clone()); 124 | 125 | let service_object_b = Box::new(SimpleB::new()) as Box; 126 | proxy_a.service_object_as_argument(ServiceRef::Export(ServiceToExport::new(service_object_b))); 127 | 128 | drop(proxy_a); 129 | drop(port) 130 | } 131 | 132 | #[test] 133 | fn recursive_service_object() { 134 | init_logger(); 135 | 136 | let port = Arc::new(TestPort::new()); 137 | let mut proxy_a = create_proxy_a(port.clone()); 138 | let mut proxy_as = Vec::new(); 139 | proxy_as.push(Arc::clone(&proxy_a)); 140 | 141 | for i in 0..10 { 142 | assert_eq!(proxy_a.get_recursion_count(), i); 143 | proxy_a = proxy_a 144 | .recursive_service_object() 145 | .unwrap_import() 146 | .into_proxy(); 147 | proxy_as.push(Arc::clone(&proxy_a)); 148 | } 149 | assert_eq!(proxy_a.get_recursion_count(), 10); 150 | 151 | let proxy_b: Box = proxy_a 152 | .service_object_as_return() 153 | .unwrap_import() 154 | .into_proxy(); 155 | proxy_b.inc(); 156 | assert_eq!(proxy_b.get(), 1); 157 | 158 | // proxy_a + proxy_b + recursive 10 proxy_a = 12 159 | assert_eq!(port.register_len(), 12); 160 | 161 | drop(proxy_as); 162 | drop(proxy_a); 163 | drop(proxy_b); 164 | drop(port) 165 | } 166 | -------------------------------------------------------------------------------- /remote-trait-object/src/transport.rs: -------------------------------------------------------------------------------- 1 | //! Abstractions of a **transport** that carries out an actual communication for `remote-trait-object`. 2 | //! 3 | //! You have to implement these traits in your own requirement, to use `remote-trait-object` over them. 4 | //! It can be ordinary in-process communication, inter-process communication, or even networking over 5 | //! different machines. 6 | 7 | pub(crate) mod multiplex; 8 | 9 | /// An error that can be returned in [`send()`] or [`recv()`]. 10 | /// 11 | /// Note that only `Timeout` and `Termination` will be handled specially by the `remote-trait-object` context. 12 | /// All other errors must be wrapped as `Custom`, and it will be just conveyed to the user. 13 | /// 14 | /// [`send()`]: trait.TransportSend.html#tymethod.send 15 | /// [`recv()`]: trait.TransportRecv.html#tymethod.recv 16 | #[derive(Clone, Debug, PartialEq)] 17 | pub enum TransportError { 18 | /// An error that indicates that your call to `send()` or `recv()` can't be finished within the timeout you set. 19 | TimeOut, 20 | 21 | /// An error that indicates that you have called [`terminate()`] of the spawned [`Terminate`] from the object you're calling a method of. 22 | /// 23 | /// [`Terminate`]: trait.Terminate.html 24 | /// [`terminate()`]: trait.Terminate.html#tymethod.terminate 25 | Termination, 26 | 27 | // TODO: Provide an appropriate type for this 28 | /// An opaque error that will be just passed to the user. 29 | Custom, 30 | } 31 | 32 | /// An abstraction of a sending half of the transport 33 | /// 34 | /// All outgoing packets will be delivered to a single instance of this trait, which has been given 35 | /// when [`Context`] is created. 36 | /// 37 | /// [`Context`]: ../struct.Context.html 38 | pub trait TransportSend: Sync + Send + std::fmt::Debug { 39 | /// Sends a packet with an optional timeout. 40 | fn send(&self, data: &[u8], timeout: Option) 41 | -> Result<(), TransportError>; 42 | 43 | /// Creates a terminate switch that can be sent to another thread 44 | fn create_terminator(&self) -> Box; 45 | } 46 | 47 | /// An abstraction of a receiving half of the transport 48 | /// 49 | /// All incoming packets will be delivered by a single instance of this trait, which has been given 50 | /// when [`Context`] is created. 51 | /// 52 | /// [`Context`]: ../struct.Context.html 53 | pub trait TransportRecv: Send { 54 | /// Receives a packet with an optional timeout. 55 | /// 56 | /// Note that it is not guaranteed to receive remaining data after the counter end has 57 | /// been closed earlier. You should assume that you will receive Err(Custom) in such case. 58 | fn recv(&self, timeout: Option) -> Result, TransportError>; 59 | 60 | /// Creates a terminate switch that can be sent to another thread 61 | fn create_terminator(&self) -> Box; 62 | } 63 | 64 | /// A switch that can be separately managed by another thread. 65 | /// 66 | /// This is the only way to wake up a blocking [`send()`] or [`recv()`] by yourself. (Not by the other end) 67 | /// [`TransportSend`] and [`TransportRecv`] must be able to provide such a switch that triggers [`Termination`] error for its own [`send()`] or [`recv()`]. 68 | /// 69 | /// [`TransportSend`]: trait.TransportSend.html 70 | /// [`TransportRecv`]: trait.TransportRecv.html 71 | /// [`send()`]: trait.TransportSend.html#tymethod.send 72 | /// [`recv()`]: trait.TransportRecv.html#tymethod.recv 73 | /// [`Termination`]: enum.TransportError.html#variant.Termination 74 | pub trait Terminate: Send { 75 | /// Wakes up block on recv() or send() 76 | fn terminate(&self); 77 | } 78 | -------------------------------------------------------------------------------- /remote-trait-object/src/transport/multiplex.rs: -------------------------------------------------------------------------------- 1 | use crate::packet::PacketView; 2 | use crate::transport::{Terminate, TransportError, TransportRecv}; 3 | use crate::Config; 4 | use crossbeam::channel::{self, Receiver, Sender}; 5 | use parking_lot::Mutex; 6 | use std::thread; 7 | 8 | pub struct MultiplexedRecv { 9 | recv: Receiver, TransportError>>, 10 | } 11 | 12 | impl TransportRecv for MultiplexedRecv { 13 | fn recv(&self, timeout: Option) -> Result, TransportError> { 14 | if let Some(timeout) = timeout { 15 | self.recv.recv_timeout(timeout).unwrap() 16 | } else { 17 | self.recv.recv().unwrap() 18 | } 19 | } 20 | 21 | fn create_terminator(&self) -> Box { 22 | unreachable!() 23 | } 24 | } 25 | 26 | #[derive(Debug)] 27 | pub enum ForwardResult { 28 | Request, 29 | Response, 30 | } 31 | 32 | pub trait Forward { 33 | fn forward(data: PacketView) -> ForwardResult; 34 | } 35 | 36 | pub struct MultiplexResult { 37 | pub request_recv: MultiplexedRecv, 38 | pub response_recv: MultiplexedRecv, 39 | pub multiplexer: Multiplexer, 40 | } 41 | 42 | pub struct Multiplexer { 43 | receiver_thread: Option>, 44 | /// Here Mutex is used to make the Multiplxer Sync, while dyn Terminate isn't. 45 | receiver_terminator: Option>>, 46 | } 47 | 48 | impl Multiplexer { 49 | pub fn multiplex( 50 | config: Config, 51 | transport_recv: TransportReceiver, 52 | ) -> MultiplexResult 53 | where 54 | TransportReceiver: TransportRecv + 'static, 55 | Forwarder: Forward, 56 | { 57 | let (request_send, request_recv) = channel::bounded(1); 58 | let (response_send, response_recv) = channel::bounded(1); 59 | let receiver_terminator: Option>> = 60 | Some(Mutex::new(transport_recv.create_terminator())); 61 | 62 | let receiver_thread = thread::Builder::new() 63 | .name(format!("[{}] receiver multiplexer", config.name)) 64 | .spawn(move || { 65 | receiver_loop::( 66 | transport_recv, 67 | request_send, 68 | response_send, 69 | ) 70 | }) 71 | .unwrap(); 72 | 73 | MultiplexResult { 74 | request_recv: MultiplexedRecv { recv: request_recv }, 75 | response_recv: MultiplexedRecv { 76 | recv: response_recv, 77 | }, 78 | multiplexer: Multiplexer { 79 | receiver_thread: Some(receiver_thread), 80 | receiver_terminator, 81 | }, 82 | } 83 | } 84 | 85 | pub fn shutdown(mut self) { 86 | self.receiver_terminator 87 | .take() 88 | .unwrap() 89 | .into_inner() 90 | .terminate(); 91 | self.receiver_thread.take().unwrap().join().unwrap(); 92 | } 93 | 94 | pub fn wait(mut self, _timeout: Option) -> Result<(), Self> { 95 | self.receiver_thread.take().unwrap().join().unwrap(); 96 | Ok(()) 97 | } 98 | } 99 | 100 | fn receiver_loop( 101 | transport_recv: Receiver, 102 | request_send: Sender, TransportError>>, 103 | response_send: Sender, TransportError>>, 104 | ) { 105 | loop { 106 | let message = match transport_recv.recv(None) { 107 | Err(err) => { 108 | request_send.send(Err(err.clone())).unwrap(); 109 | response_send.send(Err(err)).unwrap(); 110 | return; 111 | } 112 | Ok(data) => data, 113 | }; 114 | 115 | let packet_view = PacketView::new(&message); 116 | trace!("Receive message in multiplex {}", packet_view); 117 | let forward_result = Forwarder::forward(packet_view); 118 | 119 | match forward_result { 120 | ForwardResult::Request => request_send.send(Ok(message)).unwrap(), 121 | ForwardResult::Response => response_send.send(Ok(message)).unwrap(), 122 | } 123 | } 124 | } 125 | 126 | impl Drop for Multiplexer { 127 | fn drop(&mut self) { 128 | assert!(self.receiver_thread.is_none(), "Please call shutdown"); 129 | } 130 | } 131 | --------------------------------------------------------------------------------