├── .github └── workflows │ └── main.yml ├── .gitignore ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── benches ├── ipc.rs ├── ipc_receiver_set.rs ├── ipc_shared_mem.rs ├── platform.rs └── struct_ipc.rs ├── rustfmt.toml ├── src ├── asynch.rs ├── bin │ └── spawn_client_test_helper.rs ├── ipc.rs ├── lib.rs ├── platform │ ├── inprocess │ │ └── mod.rs │ ├── macos │ │ ├── mach_sys.rs │ │ └── mod.rs │ ├── mod.rs │ ├── test.rs │ ├── unix │ │ └── mod.rs │ └── windows │ │ ├── aliased_cell.rs │ │ ├── mod.rs │ │ └── tests.rs ├── router.rs └── test.rs └── tests └── integration_test.rs /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | workflow_dispatch: 8 | merge_group: 9 | types: [checks_requested] 10 | 11 | jobs: 12 | test: 13 | name: ${{ format('{0} {1}', matrix.platform.target, matrix.features)}} 14 | runs-on: ${{ matrix.platform.os }} 15 | env: 16 | RUST_BACKTRACE: 1 17 | strategy: 18 | matrix: 19 | platform: 20 | - { target: aarch64-apple-darwin, os: macos-14 } 21 | - { target: x86_64-apple-darwin, os: macos-13 } 22 | - { target: x86_64-unknown-linux-gnu, os: ubuntu-latest } 23 | - { target: x86_64-pc-windows-msvc, os: windows-latest } 24 | - { target: i686-pc-windows-msvc, os: windows-latest } 25 | features: ["", "force-inprocess", "async"] 26 | steps: 27 | - uses: actions/checkout@v4 28 | 29 | - name: Install stable toolchain 30 | uses: dtolnay/rust-toolchain@stable 31 | with: 32 | components: rustfmt, clippy 33 | targets: ${{ matrix.platform.target }} 34 | 35 | - name: rustfmt 36 | run: cargo fmt --check 37 | 38 | - name: clippy 39 | uses: servo/actions/cargo-annotation@main 40 | with: 41 | cargo-command: clippy --features '${{ matrix.features }}' --target '${{ matrix.platform.target }}' 42 | with-annotation: ${{ matrix.platform.target == 'x86_64-unknown-linux-gnu' && matrix.features == '' }} 43 | 44 | - name: Cargo test 45 | run: cargo test --features "${{ matrix.features }} enable-slow-tests" --target ${{ matrix.platform.target }} 46 | 47 | - name: Cargo test benches 48 | run: cargo test --benches --features "${{ matrix.features }}" --target ${{ matrix.platform.target }} 49 | 50 | build_result: 51 | name: Result 52 | runs-on: ubuntu-latest 53 | if: always() 54 | needs: 55 | - "test" 56 | 57 | steps: 58 | - name: Success 59 | run: exit 0 60 | if: ${{ !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') }} 61 | - name: Failure 62 | run: exit 1 63 | if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.swp 3 | *.swo 4 | target 5 | Cargo.lock 6 | .idea -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ipc-channel" 3 | version = "0.21.0" 4 | description = "A multiprocess drop-in replacement for Rust channels" 5 | authors = ["The Servo Project Developers"] 6 | license = "MIT OR Apache-2.0" 7 | repository = "https://github.com/servo/ipc-channel" 8 | edition = "2021" 9 | 10 | [[bench]] 11 | name = "platform" 12 | harness = false 13 | 14 | [[bench]] 15 | name = "ipc" 16 | harness = false 17 | 18 | [[bench]] 19 | name = "ipc_receiver_set" 20 | harness = false 21 | 22 | [[bench]] 23 | name = "ipc_shared_mem" 24 | harness = false 25 | 26 | [[bench]] 27 | name = "struct_ipc" 28 | harness = false 29 | 30 | [features] 31 | default = [] 32 | force-inprocess = [] 33 | async = ["dep:futures-core", "dep:futures-channel"] 34 | win32-trace = [] 35 | enable-slow-tests = [] 36 | 37 | [dependencies] 38 | bincode = "1" 39 | crossbeam-channel = "0.5" 40 | fnv = "1.0.3" 41 | futures-channel = { version = "0.3.31", optional = true } 42 | futures-core = { version = "0.3.31", optional = true } 43 | libc = "0.2.162" 44 | serde_core = "1.0" 45 | uuid = { version = "1", features = ["v4"] } 46 | 47 | [target.'cfg(any(target_os = "linux", target_os = "openbsd", target_os = "freebsd", target_os = "illumos"))'.dependencies] 48 | mio = { version = "1.0", default-features = false, features = ["os-ext"] } 49 | tempfile = "3.4" 50 | 51 | [target.'cfg(target_os = "macos")'.dependencies] 52 | rand = "0.9" 53 | 54 | [dev-dependencies] 55 | serde = { version = "1.0", features = ["rc"] } 56 | crossbeam-utils = "0.8" 57 | futures-test = "0.3" 58 | static_assertions = "1.1.0" 59 | criterion = { version = "0.5", features = ["html_reports"] } 60 | rand = "0.9.2" 61 | 62 | [target.'cfg(target_os = "windows")'.dependencies.windows] 63 | version = "0.61" 64 | features = [ 65 | "Win32_Foundation", 66 | "Win32_System_WindowsProgramming", 67 | "Win32_System_Threading", 68 | "Win32_System_Pipes", 69 | "Win32_System_Memory", 70 | "Win32_System_IO", 71 | "Win32_Storage_FileSystem", 72 | "Win32_Security", 73 | ] 74 | -------------------------------------------------------------------------------- /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 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012-2013 Mozilla Foundation 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | `ipc-channel` is an inter-process implementation of Rust channels (which were inspired by CSP[^CSP]). 2 | 3 | A Rust channel is a unidirectional, FIFO queue of messages which can be used to send messages between threads in a single operating system process. 4 | For an excellent introduction to Rust channels, see [Using Message Passing to Transfer Data Between Threads](https://doc.rust-lang.org/stable/book/ch16-02-message-passing.html) in the Rust reference. 5 | 6 | `ipc-channel` extends Rust channels to support inter-process communication (IPC) in a single operating system instance. The `serde` library is used to serialize and deserialize messages sent over `ipc-channel`. 7 | 8 | As much as possible, `ipc-channel` has been designed to be a drop-in replacement for Rust channels. The mapping from the Rust channel APIs to `ipc-channel` APIs is as follows: 9 | 10 | * `channel()` → `ipc::channel().unwrap()` 11 | * `Sender` → `ipc::IpcSender` (requires `T: Serialize`) 12 | * `Receiver` → `ipc::IpcReceiver` (requires `T: Deserialize`) 13 | 14 | Note that both `IpcSender` and `IpcReceiver` implement `Serialize` and `Deserialize`, so you can send IPC channels over IPC channels freely, just as you can with Rust channels. 15 | 16 | The easiest way to make your types implement `Serialize` and `Deserialize` is to use the `serde_macros` crate from crates.io as a plugin and then annotate the types you want to send with `#[derive(Deserialize, Serialize])`. In many cases, that's all you need to do — the compiler generates all the tedious boilerplate code needed to serialize and deserialize instances of your types. 17 | 18 | ## Semantic differences from Rust channels 19 | 20 | * Rust channels can be either unbounded or bounded whereas ipc-channels are always unbounded and `send()` never blocks. 21 | * Rust channels do not consume OS IPC resources whereas ipc-channels consume IPC resources such as sockets, file descriptors, shared memory segments, named pipes, and such like, depending on the OS. 22 | * Rust channels transfer ownership of messages whereas ipc-channels serialize and deserialize messages. 23 | * Rust channels are type safe whereas ipc-channels depend on client and server programs using identical message types (or at least message types with compatible serial forms). 24 | 25 | ## Bootstrapping channels between processes 26 | 27 | `ipc-channel` provides a one-shot server to help establish a channel between two processes. When a one-shot server is created, a server name is generated and returned along with the server. 28 | 29 | The client process calls `connect()` passing the server name and this returns the sender end of an ipc-channel from 30 | the client to the server. Note that there is a restriction in `ipc-channel`: `connect()` may be called at most once per one-shot server. 31 | 32 | The server process calls `accept()` on the server to accept connect requests from clients. `accept()` blocks until a client has connected to the server and sent a message. It then returns a pair consisting of the receiver end of the ipc-channel from client to server and the first message received from the client. 33 | 34 | So, in order to bootstrap an IPC channel between processes, you create an instance of the `IpcOneShotServer` type, pass the resultant server name into the client process (perhaps via an environment variable or command line flag), and connect to the server in the client. See `spawn_one_shot_server_client()` in `integration_test.rs` for an example of how to do this using a command to spawn the client process and `cross_process_embedded_senders_fork()` in `test.rs` for an example of how to do this using Unix `fork()`[^fork] to create the client process. 35 | 36 | ## Testing 37 | 38 | To run the tests, issue: 39 | 40 | ```console 41 | cargo test 42 | ``` 43 | 44 | Some tests are platform dependent, so for completeness it would be necessary to run the tests on all platforms: 45 | 46 | * iOS 47 | * macOS† 48 | * Unix variants: 49 | * Android 50 | * FreeBSD 51 | * Illumos 52 | * Linux (Ubuntu†) 53 | * OpenBSD 54 | * WASI 55 | * Windows† 56 | 57 | The platforms marked † are covered by CI. 58 | 59 | To run the benchmarks, issue: 60 | 61 | ```console 62 | cargo bench 63 | ``` 64 | 65 | ## Implementation overview 66 | 67 | `ipc-channel` is implemented in terms of native IPC primitives: file descriptor passing over Unix sockets on Unix variants, Mach ports on macOS, and named pipes on Windows. 68 | 69 | One-shot server names are implemented as a file system path (for Unix variants, with the file system path bound to the socket) or other kinds of generated names on macOS and Windows. 70 | 71 | ## Major missing features 72 | 73 | * Each one-shot server accepts only one client connect request. This is fine if you simply want to use this API to split your application up into a fixed number of mutually untrusting processes, but it's not suitable for implementing a system service. An API for multiple clients may be added later if demand exists for it. 74 | 75 | ## Related 76 | 77 | * [Rust channel](https://doc.rust-lang.org/std/sync/mpsc/index.html): MPSC (multi-producer, single-consumer) channels in the Rust standard library. The implementation 78 | consists of a single consumer wrapper of a port of Crossbeam channel. 79 | * [Crossbeam channel](https://github.com/crossbeam-rs/crossbeam/tree/master/crossbeam-channel): extends Rust channels to be more like their Go counterparts. Crossbeam channels are MPMC (multi-producer, multi-consumer) 80 | * [Channels](https://docs.rs/channels/latest/channels/): provides Sender and Receiver types for communicating with a channel-like API across generic IO streams. 81 | 82 | [^CSP]: Tony Hoare conceived Communicating Sequential Processes (CSP) as a concurrent programming language. 83 | Stephen Brookes and A.W. Roscoe developed a sound mathematical basis for CSP as a process algebra. 84 | CSP can now be used to reason about concurrency and to verify concurrency properties using model checkers such as FDR4. 85 | Go channels were also inspired by CSP. 86 | 87 | [^fork]: `fork()` has a number of semantic rough edges and is not recommended for general use. See "A fork() in the road" by Andrew Baumann _et al._, Proceedings of the Workshop on Hot Topics in Operating Systems, ACM, 2019. ([PDF](https://www.microsoft.com/en-us/research/uploads/prod/2019/04/fork-hotos19.pdf)) 88 | -------------------------------------------------------------------------------- /benches/ipc.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::identity_op)] 2 | use criterion::{criterion_group, criterion_main, Criterion}; 3 | use ipc_channel::ipc; 4 | 5 | /// Allows doing multiple inner iterations per bench.iter() run. 6 | /// 7 | /// This is mostly to amortise the overhead of spawning a thread in the benchmark 8 | /// when sending larger messages (that might be fragmented). 9 | /// 10 | /// Note that you need to compensate the displayed results 11 | /// for the proportionally longer runs yourself, 12 | /// as the benchmark framework doesn't know about the inner iterations... 13 | const ITERATIONS: usize = 1; 14 | 15 | fn transfer_empty(criterion: &mut Criterion) { 16 | criterion.bench_function("transfer_empty", |bencher| { 17 | let (tx, rx) = ipc::channel().unwrap(); 18 | bencher.iter(|| { 19 | for _ in 0..ITERATIONS { 20 | tx.send(()).unwrap(); 21 | rx.recv().unwrap() 22 | } 23 | }); 24 | }); 25 | } 26 | 27 | fn transfer_senders(criterion: &mut Criterion) { 28 | criterion.bench_function(&format!("transfer_senders_{COUNT:02}"), |bencher| { 29 | let (main_tx, main_rx) = ipc::channel().unwrap(); 30 | let transfer_txs: Vec<_> = (0..COUNT) 31 | .map(|_| ipc::channel::<()>().unwrap()) 32 | .map(|(tx, _)| tx) 33 | .collect(); 34 | let mut transfer_txs = Some(transfer_txs); 35 | bencher.iter(|| { 36 | for _ in 0..ITERATIONS { 37 | main_tx.send(transfer_txs.take().unwrap()).unwrap(); 38 | transfer_txs = Some(main_rx.recv().unwrap()); 39 | } 40 | }); 41 | }); 42 | } 43 | 44 | fn transfer_receivers(criterion: &mut Criterion) { 45 | criterion.bench_function(&format!("transfer_receivers_{COUNT:02}"), |bencher| { 46 | let (main_tx, main_rx) = ipc::channel().unwrap(); 47 | let transfer_rxs: Vec<_> = (0..COUNT) 48 | .map(|_| ipc::channel::<()>().unwrap()) 49 | .map(|(_, rx)| rx) 50 | .collect(); 51 | let mut transfer_rxs = Some(transfer_rxs); 52 | bencher.iter(|| { 53 | for _ in 0..ITERATIONS { 54 | main_tx.send(transfer_rxs.take().unwrap()).unwrap(); 55 | transfer_rxs = Some(main_rx.recv().unwrap()); 56 | } 57 | }); 58 | }); 59 | } 60 | 61 | criterion_group!( 62 | benches, 63 | transfer_empty, 64 | transfer_senders<0>, 65 | transfer_senders<1>, 66 | transfer_senders<8>, 67 | transfer_senders<64>, 68 | transfer_receivers<0>, 69 | transfer_receivers<1>, 70 | transfer_receivers<8>, 71 | transfer_receivers<64>, 72 | ); 73 | 74 | criterion_main!(benches); 75 | -------------------------------------------------------------------------------- /benches/ipc_receiver_set.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::identity_op)] 2 | use criterion::{criterion_group, criterion_main, Criterion}; 3 | 4 | /// Allows doing multiple inner iterations per bench.iter() run. 5 | /// 6 | /// This is mostly to amortise the overhead of spawning a thread in the benchmark 7 | /// when sending larger messages (that might be fragmented). 8 | /// 9 | /// Note that you need to compensate the displayed results 10 | /// for the proportionally longer runs yourself, 11 | /// as the benchmark framework doesn't know about the inner iterations... 12 | const ITERATIONS: usize = 1; 13 | 14 | use ipc_channel::ipc::{self, IpcReceiverSet}; 15 | 16 | /// Benchmark selecting over a set of `n` receivers, 17 | /// with `to_send` of them actually having pending data. 18 | fn bench_send_on_m_of_n(criterion: &mut Criterion) { 19 | criterion.bench_function(&format!("bench_send_on_{TO_SEND}_of_{N}"), |bencher| { 20 | let mut senders = Vec::with_capacity(N); 21 | let mut rx_set = IpcReceiverSet::new().unwrap(); 22 | for _ in 0..N { 23 | let (tx, rx) = ipc::channel().unwrap(); 24 | rx_set.add(rx).unwrap(); 25 | senders.push(tx); 26 | } 27 | bencher.iter(|| { 28 | for _ in 0..ITERATIONS { 29 | for tx in senders.iter().take(TO_SEND) { 30 | tx.send(()).unwrap(); 31 | } 32 | let mut received = 0; 33 | while received < TO_SEND { 34 | received += rx_set.select().unwrap().len(); 35 | } 36 | } 37 | }); 38 | }); 39 | } 40 | 41 | fn create_set_of_n() -> IpcReceiverSet { 42 | let mut rx_set = IpcReceiverSet::new().unwrap(); 43 | for _ in 0..N { 44 | let (_, rx) = ipc::channel::<()>().unwrap(); 45 | rx_set.add(rx).unwrap(); 46 | } 47 | rx_set 48 | } 49 | 50 | fn create_and_destroy_set_of_n(criterion: &mut Criterion) { 51 | criterion.bench_function(&format!("create_and_destroy_set_of_{N}"), |bencher| { 52 | bencher.iter(|| { 53 | for _ in 0..ITERATIONS { 54 | create_set_of_n::(); 55 | } 56 | }); 57 | }); 58 | } 59 | 60 | // Benchmark performance of removing closed receivers from set. 61 | // This also includes the time for adding receivers, 62 | // as there is no way to measure the removing in isolation. 63 | fn add_and_remove_n_closed_receivers(criterion: &mut Criterion) { 64 | criterion.bench_function(&format!("add_and_remove_{N}_closed_receivers"), |bencher| { 65 | bencher.iter(|| { 66 | for _ in 0..ITERATIONS { 67 | // We could keep adding/removing senders to the same set, 68 | // instead of creating a new one in each iteration. 69 | // However, this would actually make the results harder to compare... 70 | let mut rx_set = create_set_of_n::(); 71 | 72 | let mut dropped_count = 0; 73 | while dropped_count < N { 74 | // On `select()`, receivers with a "ClosedChannel" event will be closed, 75 | // and automatically dropped from the set. 76 | dropped_count += rx_set.select().unwrap().len(); 77 | } 78 | } 79 | }); 80 | }); 81 | } 82 | 83 | criterion_group!( 84 | benches, 85 | bench_send_on_m_of_n<1,1>, 86 | bench_send_on_m_of_n<1,5>, 87 | bench_send_on_m_of_n<2,5>, 88 | bench_send_on_m_of_n<5,5>, 89 | bench_send_on_m_of_n<1,20>, 90 | bench_send_on_m_of_n<5,20>, 91 | bench_send_on_m_of_n<20,20>, 92 | bench_send_on_m_of_n<1,100>, 93 | bench_send_on_m_of_n<5,100>, 94 | bench_send_on_m_of_n<20,100>, 95 | bench_send_on_m_of_n<100,100>, 96 | create_and_destroy_set_of_n<0>, 97 | create_and_destroy_set_of_n<1>, 98 | create_and_destroy_set_of_n<10>, 99 | create_and_destroy_set_of_n<100>, 100 | add_and_remove_n_closed_receivers<1>, 101 | add_and_remove_n_closed_receivers<10>, 102 | add_and_remove_n_closed_receivers<100>, 103 | ); 104 | criterion_main!(benches); 105 | -------------------------------------------------------------------------------- /benches/ipc_shared_mem.rs: -------------------------------------------------------------------------------- 1 | use std::time::Instant; 2 | 3 | use criterion::{criterion_group, criterion_main, Criterion}; 4 | use ipc_channel::ipc::{self, IpcSharedMemory}; 5 | 6 | #[inline] 7 | fn on_recv(mut ism: IpcSharedMemory) -> IpcSharedMemory { 8 | if MUT { 9 | let data = unsafe { ism.deref_mut() }; 10 | for d in data { 11 | *d += 1; 12 | } 13 | ism 14 | } else { 15 | let mut data = ism.to_vec(); 16 | for d in &mut data { 17 | *d += 1; 18 | } 19 | IpcSharedMemory::from_bytes(&data) 20 | } 21 | } 22 | 23 | fn ping_pong_mut_shared_mem( 24 | criterion: &mut Criterion, 25 | ) { 26 | criterion.bench_function( 27 | &format!( 28 | "ping_pong_shared_mem{}_{SIZE}_{COUNT}", 29 | if MUT { "_mut" } else { "" } 30 | ), 31 | |bencher| { 32 | bencher.iter_custom(|_| { 33 | let (tx1, rx1) = ipc::channel().unwrap(); 34 | let (tx2, rx2) = ipc::channel().unwrap(); 35 | let tx = tx1.clone(); 36 | let _t1 = std::thread::spawn(move || { 37 | for _i in 0..=COUNT / 2 { 38 | tx2.send(on_recv::(rx1.recv().unwrap())).unwrap(); 39 | } 40 | }); 41 | let t2 = std::thread::spawn(move || { 42 | for _i in 0..COUNT / 2 { 43 | tx1.send(on_recv::(rx2.recv().unwrap())).unwrap(); 44 | } 45 | rx2.recv().unwrap().to_vec() 46 | }); 47 | let start = Instant::now(); 48 | tx.send(IpcSharedMemory::from_byte(0, SIZE)).unwrap(); 49 | let data = t2.join().unwrap(); 50 | let duration = start.elapsed(); 51 | assert!(data.iter().all(|d| *d == (COUNT / 2) * 2 + 1)); 52 | duration 53 | }); 54 | }, 55 | ); 56 | } 57 | 58 | criterion_group!( 59 | benches, 60 | ping_pong_mut_shared_mem, 61 | ping_pong_mut_shared_mem, 62 | ping_pong_mut_shared_mem, 63 | ping_pong_mut_shared_mem, 64 | ); 65 | criterion_main!(benches); 66 | -------------------------------------------------------------------------------- /benches/platform.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::identity_op)] 2 | use criterion::{criterion_group, criterion_main, Criterion}; 3 | use ipc_channel::platform; 4 | use std::sync::{mpsc, Mutex}; 5 | 6 | /// Allows doing multiple inner iterations per bench.iter() run. 7 | /// 8 | /// This is mostly to amortise the overhead of spawning a thread in the benchmark 9 | /// when sending larger messages (that might be fragmented). 10 | /// 11 | /// Note that you need to compensate the displayed results 12 | /// for the proportionally longer runs yourself, 13 | /// as the benchmark framework doesn't know about the inner iterations... 14 | const ITERATIONS: usize = 1; 15 | 16 | fn create_channel(criterion: &mut Criterion) { 17 | criterion.bench_function("create_channel", |bencher| { 18 | bencher.iter(|| { 19 | for _ in 0..ITERATIONS { 20 | platform::channel().unwrap(); 21 | } 22 | }); 23 | }); 24 | } 25 | 26 | fn transfer_data(criterion: &mut Criterion) { 27 | criterion.bench_function(&format!("transfer_data_{SIZE}"), |bencher| { 28 | let data: Vec = (0..SIZE).map(|i| (i % 251) as u8).collect(); 29 | let (tx, rx) = platform::channel().unwrap(); 30 | 31 | let (wait_tx, wait_rx) = mpsc::channel(); 32 | let wait_rx = Mutex::new(wait_rx); 33 | 34 | if SIZE > platform::OsIpcSender::get_max_fragment_size() { 35 | bencher.iter(|| { 36 | crossbeam_utils::thread::scope(|scope| { 37 | let tx = tx.clone(); 38 | scope.spawn(|_| { 39 | let wait_rx = wait_rx.lock().unwrap(); 40 | let tx = tx; 41 | for _ in 0..ITERATIONS { 42 | tx.send(&data, vec![], vec![]).unwrap(); 43 | if ITERATIONS > 1 { 44 | // Prevent beginning of the next send 45 | // from overlapping with receive of last fragment, 46 | // as otherwise results of runs with a large tail fragment 47 | // are significantly skewed. 48 | wait_rx.recv().unwrap(); 49 | } 50 | } 51 | }); 52 | for _ in 0..ITERATIONS { 53 | rx.recv().unwrap(); 54 | if ITERATIONS > 1 { 55 | wait_tx.send(()).unwrap(); 56 | } 57 | } 58 | // For reasons mysterious to me, 59 | // not returning a value *from every branch* 60 | // adds some 100 ns or so of overhead to all results -- 61 | // which is quite significant for very short tests... 62 | 0 63 | }) 64 | }); 65 | } else { 66 | bencher.iter(|| { 67 | for _ in 0..ITERATIONS { 68 | tx.send(&data, vec![], vec![]).unwrap(); 69 | rx.recv().unwrap(); 70 | } 71 | 0 72 | }); 73 | } 74 | }); 75 | } 76 | 77 | criterion_group!( 78 | benches, 79 | create_channel, 80 | transfer_data<1>, 81 | transfer_data<2>, 82 | transfer_data<4>, 83 | transfer_data<8>, 84 | transfer_data<16>, 85 | transfer_data<32>, 86 | transfer_data<64>, 87 | transfer_data<128>, 88 | transfer_data<256>, 89 | transfer_data<512>, 90 | transfer_data<{ 1 * 1024 }>, 91 | transfer_data<{ 2 * 1024 }>, 92 | transfer_data<{ 4 * 1024 }>, 93 | transfer_data<{ 8 * 1024 }>, 94 | transfer_data<{ 16 * 1024 }>, 95 | transfer_data<{ 32 * 1024 }>, 96 | transfer_data<{ 64 * 1024 }>, 97 | transfer_data<{ 128 * 1024 }>, 98 | transfer_data<{ 256 * 1024 }>, 99 | transfer_data<{ 512 * 1024 }>, 100 | transfer_data<{ 1 * 1024 * 1024 }>, 101 | transfer_data<{ 2 * 1024 * 1024 }>, 102 | transfer_data<{ 4 * 1024 * 1024 }>, 103 | transfer_data<{ 8 * 1024 * 1024 }>, 104 | ); 105 | criterion_main!(benches); 106 | -------------------------------------------------------------------------------- /benches/struct_ipc.rs: -------------------------------------------------------------------------------- 1 | use criterion::{criterion_group, criterion_main, Criterion}; 2 | use ipc_channel::ipc; 3 | use rand::prelude::*; 4 | use serde::{Deserialize, Serialize}; 5 | 6 | #[derive(Clone, Serialize, Deserialize)] 7 | enum TestSmall { 8 | Msg0, 9 | Msg1, 10 | Msg2(u32, u32), 11 | Msg3(u32, u32, u32), 12 | Msg4(String), 13 | Msg5, 14 | Msg6, 15 | } 16 | 17 | trait NewFromRandom { 18 | fn new(rng: &mut ThreadRng) -> Self; 19 | } 20 | 21 | impl NewFromRandom for TestSmall { 22 | fn new(rng: &mut ThreadRng) -> Self { 23 | match rng.random_range(0..=6) { 24 | 0 => Self::Msg0, 25 | 1 => Self::Msg1, 26 | 2 => Self::Msg2(23, 42), 27 | 3 => Self::Msg3(23, 42, 243), 28 | 4 => Self::Msg4(String::from("This is a test string of medium size")), 29 | 5 => Self::Msg5, 30 | 6 => Self::Msg6, 31 | _ => Self::Msg6, 32 | } 33 | } 34 | } 35 | 36 | #[derive(Clone, Serialize, Deserialize)] 37 | enum TestMedium { 38 | Msg0(u32), 39 | Msg1(u32), 40 | Msg2(u32), 41 | Msg3(u32), 42 | Msg4(u32), 43 | Msg5(u32), 44 | Msg6(u32), 45 | Msg7(u32), 46 | Msg8(u32), 47 | Msg9(u32), 48 | Msg10(u32), 49 | Msg11(u32), 50 | Msg12(u32), 51 | Msg13(u32), 52 | Msg14(u32), 53 | Msg15(u32), 54 | Msg16(u32), 55 | Msg17(u32), 56 | Msg18(u32), 57 | Msg19(u32), 58 | Msg20(u32), 59 | Msg21(u32), 60 | Msg22(u32), 61 | Msg23(u32), 62 | Msg24(u32), 63 | Msg25(u32), 64 | Msg26(u32), 65 | } 66 | 67 | impl NewFromRandom for TestMedium { 68 | fn new(rng: &mut ThreadRng) -> Self { 69 | match rng.random_range(0..=26) { 70 | 0 => Self::Msg0(42), 71 | 1 => Self::Msg1(42), 72 | 2 => Self::Msg2(42), 73 | 3 => Self::Msg3(42), 74 | 4 => Self::Msg4(42), 75 | 5 => Self::Msg5(42), 76 | 6 => Self::Msg6(42), 77 | 7 => Self::Msg7(42), 78 | 8 => Self::Msg8(42), 79 | 9 => Self::Msg9(42), 80 | 10 => Self::Msg10(42), 81 | 11 => Self::Msg11(42), 82 | 12 => Self::Msg12(42), 83 | 13 => Self::Msg13(42), 84 | 14 => Self::Msg14(42), 85 | 15 => Self::Msg15(42), 86 | 16 => Self::Msg16(42), 87 | 17 => Self::Msg17(42), 88 | 18 => Self::Msg18(42), 89 | 19 => Self::Msg19(42), 90 | 20 => Self::Msg20(42), 91 | 21 => Self::Msg21(42), 92 | 22 => Self::Msg22(42), 93 | 23 => Self::Msg23(42), 94 | 24 => Self::Msg24(42), 95 | 25 => Self::Msg25(42), 96 | 26 => Self::Msg26(42), 97 | _ => Self::Msg6(42), 98 | } 99 | } 100 | } 101 | 102 | #[derive(Serialize, Deserialize)] 103 | enum TestFractured { 104 | Msg0, 105 | Msg1(Vec), 106 | Msg2( 107 | usize, 108 | usize, 109 | usize, 110 | usize, 111 | usize, 112 | usize, 113 | usize, 114 | usize, 115 | usize, 116 | usize, 117 | ), 118 | Msg3(usize), 119 | Msg4(usize, usize, usize), 120 | } 121 | 122 | impl NewFromRandom for TestFractured { 123 | fn new(rng: &mut ThreadRng) -> Self { 124 | match rng.random_range(0..=4) { 125 | 0 => Self::Msg0, 126 | 1 => Self::Msg1(vec![1, 2, 3]), 127 | 2 => Self::Msg2(1, 2, 3, 4, 5, 6, 7, 8, 9, 10), 128 | 3 => Self::Msg3(1), 129 | 4 => Self::Msg4(1, 2, 3), 130 | _ => Self::Msg3(1), 131 | } 132 | } 133 | } 134 | 135 | #[derive(Serialize, Deserialize)] 136 | enum TestNested { 137 | Msg0, 138 | Msg1(TestSmall), 139 | Msg2(TestMedium), 140 | Msg3(TestFractured), 141 | Msg4(usize), 142 | } 143 | 144 | impl NewFromRandom for TestNested { 145 | fn new(rng: &mut ThreadRng) -> Self { 146 | match rng.random_range(0..=5) { 147 | 0 => Self::Msg0, 148 | 1 => Self::Msg1(TestSmall::Msg6), 149 | 2 => Self::Msg2(TestMedium::Msg20(2)), 150 | 3 => Self::Msg3(TestFractured::Msg3(0)), 151 | 4 => Self::Msg4(2), 152 | _ => Self::Msg3(TestFractured::Msg0), 153 | } 154 | } 155 | } 156 | 157 | fn transfer_enum(criterion: &mut Criterion) 158 | where 159 | T: NewFromRandom, 160 | T: for<'de> Deserialize<'de>, 161 | T: Serialize, 162 | { 163 | criterion.bench_function("transfer_simple_struct", |bencher| { 164 | let (tx, rx) = ipc::channel().unwrap(); 165 | let mut rng = rand::rng(); 166 | 167 | bencher.iter_batched( 168 | || T::new(&mut rng), 169 | |s| { 170 | tx.send(s).unwrap(); 171 | rx.recv().unwrap(); 172 | }, 173 | criterion::BatchSize::SmallInput, 174 | ); 175 | }); 176 | } 177 | 178 | // TestSmall is a small enum 179 | // TestMedium is a medium size enum 180 | // TestFractured is an enum with varying sizes of inners 181 | // TestNested nests the enums 182 | 183 | criterion_group!( 184 | benches, 185 | transfer_enum, 186 | transfer_enum, 187 | transfer_enum, 188 | transfer_enum, 189 | ); 190 | 191 | criterion_main!(benches); 192 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | match_block_trailing_comma = true 2 | reorder_imports = true 3 | -------------------------------------------------------------------------------- /src/asynch.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Servo Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution. 3 | // 4 | // Licensed under the Apache License, Version 2.0 or the MIT license 6 | // , at your 7 | // option. This file may not be copied, modified, or distributed 8 | // except according to those terms. 9 | 10 | use crate::ipc::{ 11 | self, IpcMessage, IpcReceiver, IpcReceiverSet, IpcSelectionResult, IpcSender, OpaqueIpcReceiver, 12 | }; 13 | use futures_channel::mpsc::UnboundedReceiver; 14 | use futures_channel::mpsc::UnboundedSender; 15 | use futures_core::stream::FusedStream; 16 | use futures_core::task::Context; 17 | use futures_core::task::Poll; 18 | use futures_core::Stream; 19 | use serde_core::{Deserialize, Serialize}; 20 | use std::collections::HashMap; 21 | use std::marker::PhantomData; 22 | use std::pin::Pin; 23 | use std::sync::{LazyLock, Mutex}; 24 | use std::thread; 25 | 26 | /// A stream built from an IPC channel. 27 | pub struct IpcStream(UnboundedReceiver, PhantomData); 28 | 29 | impl Unpin for IpcStream {} 30 | 31 | // A router which routes from an IPC channel to a stream. 32 | struct Router { 33 | // Send `(ipc_recv, send)` to this router to add a route 34 | // from the IPC receiver to the sender. 35 | add_route: UnboundedSender<(OpaqueIpcReceiver, UnboundedSender)>, 36 | 37 | // Wake up the routing thread. 38 | wakeup: Mutex>, 39 | } 40 | 41 | // Lazily initialize a singleton router, 42 | // so we only end up with one routing thread per process. 43 | static ROUTER: LazyLock = LazyLock::new(|| { 44 | let (send, mut recv) = futures_channel::mpsc::unbounded(); 45 | let (waker, wakee) = ipc::channel().expect("Failed to create IPC channel"); 46 | thread::spawn(move || { 47 | let mut receivers = IpcReceiverSet::new().expect("Failed to create receiver set"); 48 | let mut senders = HashMap::>::new(); 49 | let _ = receivers.add(wakee); 50 | while let Ok(mut selections) = receivers.select() { 51 | for selection in selections.drain(..) { 52 | match selection { 53 | IpcSelectionResult::MessageReceived(id, msg) => { 54 | if let Some(sender) = senders.get(&id) { 55 | let _ = sender.unbounded_send(msg); 56 | } 57 | }, 58 | IpcSelectionResult::ChannelClosed(id) => { 59 | senders.remove(&id); 60 | }, 61 | } 62 | } 63 | if !recv.is_terminated() { 64 | while let Ok(Some((receiver, sender))) = recv.try_next() { 65 | if let Ok(id) = receivers.add_opaque(receiver) { 66 | senders.insert(id, sender); 67 | } 68 | } 69 | } 70 | } 71 | }); 72 | Router { 73 | add_route: send, 74 | wakeup: Mutex::new(waker), 75 | } 76 | }); 77 | 78 | impl IpcReceiver 79 | where 80 | T: for<'de> Deserialize<'de> + Serialize, 81 | { 82 | /// Convert this IPC receiver into a stream. 83 | pub fn to_stream(self) -> IpcStream { 84 | let opaque = self.to_opaque(); 85 | let (send, recv) = futures_channel::mpsc::unbounded(); 86 | let _ = ROUTER.add_route.unbounded_send((opaque, send)); 87 | if let Ok(waker) = ROUTER.wakeup.lock() { 88 | let _ = waker.send(()); 89 | } 90 | IpcStream(recv, PhantomData) 91 | } 92 | } 93 | 94 | impl Stream for IpcStream 95 | where 96 | T: for<'de> Deserialize<'de> + Serialize, 97 | { 98 | type Item = Result; 99 | 100 | fn poll_next(mut self: Pin<&mut Self>, ctx: &mut Context) -> Poll> { 101 | let recv = Pin::new(&mut self.0); 102 | match recv.poll_next(ctx) { 103 | Poll::Ready(Some(msg)) => Poll::Ready(Some(msg.to())), 104 | Poll::Ready(None) => Poll::Ready(None), 105 | Poll::Pending => Poll::Pending, 106 | } 107 | } 108 | } 109 | 110 | impl FusedStream for IpcStream 111 | where 112 | T: for<'de> Deserialize<'de> + Serialize, 113 | { 114 | fn is_terminated(&self) -> bool { 115 | self.0.is_terminated() 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/bin/spawn_client_test_helper.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2025 The Servo Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution. 3 | // 4 | // Licensed under the Apache License, Version 2.0 or the MIT license 6 | // , at your 7 | // option. This file may not be copied, modified, or distributed 8 | // except according to those terms. 9 | 10 | use ipc_channel::ipc::IpcSender; 11 | use std::{env, process}; 12 | 13 | /// Test executable which connects to the one-shot server with name 14 | /// passed in as an argument and then sends a test message to the 15 | /// server. 16 | fn main() { 17 | let args: Vec = env::args().collect(); 18 | let token = args.get(1).expect("missing argument"); 19 | 20 | let tx: IpcSender = IpcSender::connect(token.to_string()).expect("connect failed"); 21 | tx.send("test message".to_string()).expect("send failed"); 22 | 23 | process::exit(0); 24 | } 25 | -------------------------------------------------------------------------------- /src/ipc.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Servo Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution. 3 | // 4 | // Licensed under the Apache License, Version 2.0 or the MIT license 6 | // , at your 7 | // option. This file may not be copied, modified, or distributed 8 | // except according to those terms. 9 | 10 | use crate::platform::{self, OsIpcChannel, OsIpcReceiver, OsIpcReceiverSet, OsIpcSender}; 11 | use crate::platform::{ 12 | OsIpcOneShotServer, OsIpcSelectionResult, OsIpcSharedMemory, OsOpaqueIpcChannel, 13 | }; 14 | 15 | use bincode; 16 | use serde_core::{de::Error, Deserialize, Deserializer, Serialize, Serializer}; 17 | use std::cell::RefCell; 18 | use std::cmp::min; 19 | use std::error::Error as StdError; 20 | use std::fmt::{self, Debug, Formatter}; 21 | use std::io; 22 | use std::marker::PhantomData; 23 | use std::mem; 24 | use std::ops::Deref; 25 | use std::time::Duration; 26 | 27 | thread_local! { 28 | static OS_IPC_CHANNELS_FOR_DESERIALIZATION: RefCell> = 29 | const { RefCell::new(Vec::new()) }; 30 | 31 | static OS_IPC_SHARED_MEMORY_REGIONS_FOR_DESERIALIZATION: 32 | RefCell>> = const { RefCell::new(Vec::new()) }; 33 | 34 | static OS_IPC_CHANNELS_FOR_SERIALIZATION: RefCell> = const { RefCell::new(Vec::new()) }; 35 | 36 | static OS_IPC_SHARED_MEMORY_REGIONS_FOR_SERIALIZATION: RefCell> = 37 | const { RefCell::new(Vec::new()) } 38 | } 39 | 40 | #[derive(Debug)] 41 | pub enum IpcError { 42 | Bincode(bincode::Error), 43 | Io(io::Error), 44 | Disconnected, 45 | } 46 | 47 | impl fmt::Display for IpcError { 48 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 49 | match *self { 50 | IpcError::Bincode(ref err) => write!(fmt, "bincode error: {err}"), 51 | IpcError::Io(ref err) => write!(fmt, "io error: {err}"), 52 | IpcError::Disconnected => write!(fmt, "disconnected"), 53 | } 54 | } 55 | } 56 | 57 | impl StdError for IpcError { 58 | fn source(&self) -> Option<&(dyn StdError + 'static)> { 59 | match *self { 60 | IpcError::Bincode(ref err) => Some(err), 61 | IpcError::Io(ref err) => Some(err), 62 | IpcError::Disconnected => None, 63 | } 64 | } 65 | } 66 | 67 | #[derive(Debug)] 68 | pub enum TryRecvError { 69 | IpcError(IpcError), 70 | Empty, 71 | } 72 | 73 | impl fmt::Display for TryRecvError { 74 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 75 | match *self { 76 | TryRecvError::IpcError(ref err) => write!(fmt, "ipc error: {err}"), 77 | TryRecvError::Empty => write!(fmt, "empty"), 78 | } 79 | } 80 | } 81 | 82 | impl StdError for TryRecvError { 83 | fn source(&self) -> Option<&(dyn StdError + 'static)> { 84 | match *self { 85 | TryRecvError::IpcError(ref err) => Some(err), 86 | TryRecvError::Empty => None, 87 | } 88 | } 89 | } 90 | 91 | /// Create a connected [IpcSender] and [IpcReceiver] that 92 | /// transfer messages of a given type provided by type `T` 93 | /// or inferred by the types of messages sent by the sender. 94 | /// 95 | /// Messages sent by the sender will be available to the 96 | /// receiver even if the sender or receiver has been moved 97 | /// to a different process. In addition, receivers and senders 98 | /// may be sent over an existing channel. 99 | /// 100 | /// # Examples 101 | /// 102 | /// ``` 103 | /// # use ipc_channel::ipc; 104 | /// 105 | /// let payload = "Hello, World!".to_owned(); 106 | /// 107 | /// // Create a channel 108 | /// let (tx, rx) = ipc::channel().unwrap(); 109 | /// 110 | /// // Send data 111 | /// tx.send(payload).unwrap(); 112 | /// 113 | /// // Receive the data 114 | /// let response = rx.recv().unwrap(); 115 | /// 116 | /// assert_eq!(response, "Hello, World!".to_owned()); 117 | /// ``` 118 | /// 119 | /// [IpcSender]: struct.IpcSender.html 120 | /// [IpcReceiver]: struct.IpcReceiver.html 121 | pub fn channel() -> Result<(IpcSender, IpcReceiver), io::Error> 122 | where 123 | T: for<'de> Deserialize<'de> + Serialize, 124 | { 125 | let (os_sender, os_receiver) = platform::channel()?; 126 | let ipc_receiver = IpcReceiver { 127 | os_receiver, 128 | phantom: PhantomData, 129 | }; 130 | let ipc_sender = IpcSender { 131 | os_sender, 132 | phantom: PhantomData, 133 | }; 134 | Ok((ipc_sender, ipc_receiver)) 135 | } 136 | 137 | /// Create a connected [IpcBytesSender] and [IpcBytesReceiver]. 138 | /// 139 | /// Note: The [IpcBytesSender] transfers messages of the type `[u8]` 140 | /// and the [IpcBytesReceiver] receives a `Vec`. This sender/receiver 141 | /// type does not serialize/deserialize messages through `serde`, making 142 | /// it more efficient where applicable. 143 | /// 144 | /// # Examples 145 | /// 146 | /// ``` 147 | /// # use ipc_channel::ipc; 148 | /// 149 | /// let payload = b"'Tis but a scratch!!"; 150 | /// 151 | /// // Create a channel 152 | /// let (tx, rx) = ipc::bytes_channel().unwrap(); 153 | /// 154 | /// // Send data 155 | /// tx.send(payload).unwrap(); 156 | /// 157 | /// // Receive the data 158 | /// let response = rx.recv().unwrap(); 159 | /// 160 | /// assert_eq!(response, payload); 161 | /// ``` 162 | /// 163 | /// [IpcBytesReceiver]: struct.IpcBytesReceiver.html 164 | /// [IpcBytesSender]: struct.IpcBytesSender.html 165 | pub fn bytes_channel() -> Result<(IpcBytesSender, IpcBytesReceiver), io::Error> { 166 | let (os_sender, os_receiver) = platform::channel()?; 167 | let ipc_bytes_receiver = IpcBytesReceiver { os_receiver }; 168 | let ipc_bytes_sender = IpcBytesSender { os_sender }; 169 | Ok((ipc_bytes_sender, ipc_bytes_receiver)) 170 | } 171 | 172 | /// Receiving end of a channel using serialized messages. 173 | /// 174 | /// # Examples 175 | /// 176 | /// ## Blocking IO 177 | /// 178 | /// ``` 179 | /// # use ipc_channel::ipc; 180 | /// # 181 | /// # let (tx, rx) = ipc::channel().unwrap(); 182 | /// # 183 | /// # let q = "Answer to the ultimate question of life, the universe, and everything"; 184 | /// # 185 | /// # tx.send(q.to_owned()).unwrap(); 186 | /// let response = rx.recv().unwrap(); 187 | /// println!("Received data..."); 188 | /// # assert_eq!(response, q); 189 | /// ``` 190 | /// 191 | /// ## Non-blocking IO 192 | /// 193 | /// ``` 194 | /// # use ipc_channel::ipc; 195 | /// # 196 | /// # let (tx, rx) = ipc::channel().unwrap(); 197 | /// # 198 | /// # let answer = "42"; 199 | /// # 200 | /// # tx.send(answer.to_owned()).unwrap(); 201 | /// loop { 202 | /// match rx.try_recv() { 203 | /// Ok(res) => { 204 | /// // Do something interesting with your result 205 | /// println!("Received data..."); 206 | /// break; 207 | /// }, 208 | /// Err(_) => { 209 | /// // Do something else useful while we wait 210 | /// println!("Still waiting..."); 211 | /// } 212 | /// } 213 | /// } 214 | /// ``` 215 | /// 216 | /// ## Embedding Receivers 217 | /// 218 | /// ``` 219 | /// # use ipc_channel::ipc; 220 | /// # 221 | /// let (tx, rx) = ipc::channel().unwrap(); 222 | /// let (embedded_tx, embedded_rx) = ipc::channel().unwrap(); 223 | /// # let data = [0x45, 0x6d, 0x62, 0x65, 0x64, 0x64, 0x65, 0x64, 0x00]; 224 | /// // Send the IpcReceiver 225 | /// tx.send(embedded_rx).unwrap(); 226 | /// # embedded_tx.send(data.to_owned()).unwrap(); 227 | /// // Receive the sent IpcReceiver 228 | /// let received_rx = rx.recv().unwrap(); 229 | /// // Receive any data sent to the received IpcReceiver 230 | /// let rx_data = received_rx.recv().unwrap(); 231 | /// # assert_eq!(rx_data, data); 232 | /// ``` 233 | /// 234 | /// # Implementation details 235 | /// 236 | /// Each [IpcReceiver] is backed by the OS specific implementations of `OsIpcReceiver`. 237 | /// 238 | /// [IpcReceiver]: struct.IpcReceiver.html 239 | #[derive(Debug)] 240 | pub struct IpcReceiver { 241 | os_receiver: OsIpcReceiver, 242 | phantom: PhantomData, 243 | } 244 | 245 | impl IpcReceiver 246 | where 247 | T: for<'de> Deserialize<'de> + Serialize, 248 | { 249 | /// Blocking receive. 250 | pub fn recv(&self) -> Result { 251 | self.os_receiver.recv()?.to().map_err(IpcError::Bincode) 252 | } 253 | 254 | /// Non-blocking receive 255 | pub fn try_recv(&self) -> Result { 256 | self.os_receiver 257 | .try_recv()? 258 | .to() 259 | .map_err(IpcError::Bincode) 260 | .map_err(TryRecvError::IpcError) 261 | } 262 | 263 | /// Blocks for up to the specified duration attempting to receive a message. 264 | /// 265 | /// This may block for longer than the specified duration if the channel is busy. If your timeout 266 | /// exceeds the duration that your operating system can represent in milliseconds, this may 267 | /// block forever. At the time of writing, the smallest duration that may trigger this behavior 268 | /// is over 24 days. 269 | pub fn try_recv_timeout(&self, duration: Duration) -> Result { 270 | self.os_receiver 271 | .try_recv_timeout(duration)? 272 | .to() 273 | .map_err(IpcError::Bincode) 274 | .map_err(TryRecvError::IpcError) 275 | } 276 | 277 | /// Erase the type of the channel. 278 | /// 279 | /// Useful for adding routes to a `RouterProxy`. 280 | pub fn to_opaque(self) -> OpaqueIpcReceiver { 281 | OpaqueIpcReceiver { 282 | os_receiver: self.os_receiver, 283 | } 284 | } 285 | } 286 | 287 | impl<'de, T> Deserialize<'de> for IpcReceiver { 288 | fn deserialize(deserializer: D) -> Result 289 | where 290 | D: Deserializer<'de>, 291 | { 292 | let os_receiver = deserialize_os_ipc_receiver(deserializer)?; 293 | Ok(IpcReceiver { 294 | os_receiver, 295 | phantom: PhantomData, 296 | }) 297 | } 298 | } 299 | 300 | impl Serialize for IpcReceiver { 301 | fn serialize(&self, serializer: S) -> Result 302 | where 303 | S: Serializer, 304 | { 305 | serialize_os_ipc_receiver(&self.os_receiver, serializer) 306 | } 307 | } 308 | 309 | /// Sending end of a channel using serialized messages. 310 | /// 311 | /// 312 | /// ## Embedding Senders 313 | /// 314 | /// ``` 315 | /// # use ipc_channel::ipc; 316 | /// # 317 | /// # let (tx, rx) = ipc::channel().unwrap(); 318 | /// # let (embedded_tx, embedded_rx) = ipc::channel().unwrap(); 319 | /// # let data = [0x45, 0x6d, 0x62, 0x65, 0x64, 0x64, 0x65, 0x64, 0x00]; 320 | /// // Send the IpcSender 321 | /// tx.send(embedded_tx).unwrap(); 322 | /// // Receive the sent IpcSender 323 | /// let received_tx = rx.recv().unwrap(); 324 | /// // Send data from the received IpcSender 325 | /// received_tx.send(data.clone()).unwrap(); 326 | /// # let rx_data = embedded_rx.recv().unwrap(); 327 | /// # assert_eq!(rx_data, data); 328 | /// ``` 329 | #[derive(Debug)] 330 | pub struct IpcSender { 331 | os_sender: OsIpcSender, 332 | phantom: PhantomData, 333 | } 334 | 335 | impl Clone for IpcSender 336 | where 337 | T: Serialize, 338 | { 339 | fn clone(&self) -> IpcSender { 340 | IpcSender { 341 | os_sender: self.os_sender.clone(), 342 | phantom: PhantomData, 343 | } 344 | } 345 | } 346 | 347 | impl IpcSender 348 | where 349 | T: Serialize, 350 | { 351 | /// Create an [IpcSender] connected to a previously defined [IpcOneShotServer]. 352 | /// 353 | /// This function should not be called more than once per [IpcOneShotServer], 354 | /// otherwise the behaviour is unpredictable. 355 | /// See [issue 378](https://github.com/servo/ipc-channel/issues/378) for details. 356 | /// 357 | /// [IpcSender]: struct.IpcSender.html 358 | /// [IpcOneShotServer]: struct.IpcOneShotServer.html 359 | pub fn connect(name: String) -> Result, io::Error> { 360 | Ok(IpcSender { 361 | os_sender: OsIpcSender::connect(name)?, 362 | phantom: PhantomData, 363 | }) 364 | } 365 | 366 | /// Send data across the channel to the receiver. 367 | pub fn send(&self, data: T) -> Result<(), bincode::Error> { 368 | let mut bytes = Vec::with_capacity(4096); 369 | OS_IPC_CHANNELS_FOR_SERIALIZATION.with(|os_ipc_channels_for_serialization| { 370 | OS_IPC_SHARED_MEMORY_REGIONS_FOR_SERIALIZATION.with( 371 | |os_ipc_shared_memory_regions_for_serialization| { 372 | let old_os_ipc_channels = 373 | mem::take(&mut *os_ipc_channels_for_serialization.borrow_mut()); 374 | let old_os_ipc_shared_memory_regions = mem::take( 375 | &mut *os_ipc_shared_memory_regions_for_serialization.borrow_mut(), 376 | ); 377 | let os_ipc_shared_memory_regions; 378 | let os_ipc_channels; 379 | { 380 | bincode::serialize_into(&mut bytes, &data)?; 381 | os_ipc_channels = mem::replace( 382 | &mut *os_ipc_channels_for_serialization.borrow_mut(), 383 | old_os_ipc_channels, 384 | ); 385 | os_ipc_shared_memory_regions = mem::replace( 386 | &mut *os_ipc_shared_memory_regions_for_serialization.borrow_mut(), 387 | old_os_ipc_shared_memory_regions, 388 | ); 389 | }; 390 | Ok(self.os_sender.send( 391 | &bytes[..], 392 | os_ipc_channels, 393 | os_ipc_shared_memory_regions, 394 | )?) 395 | }, 396 | ) 397 | }) 398 | } 399 | 400 | pub fn to_opaque(self) -> OpaqueIpcSender { 401 | OpaqueIpcSender { 402 | os_sender: self.os_sender, 403 | } 404 | } 405 | } 406 | 407 | impl<'de, T> Deserialize<'de> for IpcSender { 408 | fn deserialize(deserializer: D) -> Result 409 | where 410 | D: Deserializer<'de>, 411 | { 412 | let os_sender = deserialize_os_ipc_sender(deserializer)?; 413 | Ok(IpcSender { 414 | os_sender, 415 | phantom: PhantomData, 416 | }) 417 | } 418 | } 419 | 420 | impl Serialize for IpcSender { 421 | fn serialize(&self, serializer: S) -> Result 422 | where 423 | S: Serializer, 424 | { 425 | serialize_os_ipc_sender(&self.os_sender, serializer) 426 | } 427 | } 428 | 429 | /// Collection of [IpcReceiver]s moved into the set; thus creating a common 430 | /// (and exclusive) endpoint for receiving messages on any of the added 431 | /// channels. 432 | /// 433 | /// # Examples 434 | /// 435 | /// ``` 436 | /// # use ipc_channel::ipc::{self, IpcReceiverSet, IpcSelectionResult}; 437 | /// let data = vec![0x52, 0x75, 0x73, 0x74, 0x00]; 438 | /// let (tx, rx) = ipc::channel().unwrap(); 439 | /// let mut rx_set = IpcReceiverSet::new().unwrap(); 440 | /// 441 | /// // Add the receiver to the receiver set and send the data 442 | /// // from the sender 443 | /// let rx_id = rx_set.add(rx).unwrap(); 444 | /// tx.send(data.clone()).unwrap(); 445 | /// 446 | /// // Poll the receiver set for any readable events 447 | /// for event in rx_set.select().unwrap() { 448 | /// match event { 449 | /// IpcSelectionResult::MessageReceived(id, message) => { 450 | /// let rx_data: Vec = message.to().unwrap(); 451 | /// assert_eq!(id, rx_id); 452 | /// assert_eq!(data, rx_data); 453 | /// println!("Received: {:?} from {}...", data, id); 454 | /// }, 455 | /// IpcSelectionResult::ChannelClosed(id) => { 456 | /// assert_eq!(id, rx_id); 457 | /// println!("No more data from {}...", id); 458 | /// } 459 | /// } 460 | /// } 461 | /// ``` 462 | /// [IpcReceiver]: struct.IpcReceiver.html 463 | pub struct IpcReceiverSet { 464 | os_receiver_set: OsIpcReceiverSet, 465 | } 466 | 467 | impl IpcReceiverSet { 468 | /// Create a new empty [IpcReceiverSet]. 469 | /// 470 | /// Receivers may then be added to the set with the [add] 471 | /// method. 472 | /// 473 | /// [add]: #method.add 474 | /// [IpcReceiverSet]: struct.IpcReceiverSet.html 475 | pub fn new() -> Result { 476 | Ok(IpcReceiverSet { 477 | os_receiver_set: OsIpcReceiverSet::new()?, 478 | }) 479 | } 480 | 481 | /// Add and consume the [IpcReceiver] to the set of receivers to be polled. 482 | /// [IpcReceiver]: struct.IpcReceiver.html 483 | pub fn add(&mut self, receiver: IpcReceiver) -> Result 484 | where 485 | T: for<'de> Deserialize<'de> + Serialize, 486 | { 487 | Ok(self.os_receiver_set.add(receiver.os_receiver)?) 488 | } 489 | 490 | /// Add an [OpaqueIpcReceiver] to the set of receivers to be polled. 491 | /// [OpaqueIpcReceiver]: struct.OpaqueIpcReceiver.html 492 | pub fn add_opaque(&mut self, receiver: OpaqueIpcReceiver) -> Result { 493 | Ok(self.os_receiver_set.add(receiver.os_receiver)?) 494 | } 495 | 496 | /// Wait for IPC messages received on any of the receivers in the set. The 497 | /// method will return multiple events. An event may be either a message 498 | /// received or a channel closed event. 499 | /// 500 | /// [IpcReceiver]: struct.IpcReceiver.html 501 | pub fn select(&mut self) -> Result, io::Error> { 502 | let results = self.os_receiver_set.select()?; 503 | Ok(results 504 | .into_iter() 505 | .map(|result| match result { 506 | OsIpcSelectionResult::DataReceived(os_receiver_id, ipc_message) => { 507 | IpcSelectionResult::MessageReceived(os_receiver_id, ipc_message) 508 | }, 509 | OsIpcSelectionResult::ChannelClosed(os_receiver_id) => { 510 | IpcSelectionResult::ChannelClosed(os_receiver_id) 511 | }, 512 | }) 513 | .collect()) 514 | } 515 | } 516 | 517 | /// Shared memory descriptor that will be made accessible to the receiver 518 | /// of an IPC message that contains the discriptor. 519 | /// 520 | /// # Examples 521 | /// ``` 522 | /// # use ipc_channel::ipc::{self, IpcSharedMemory}; 523 | /// # let (tx, rx) = ipc::channel().unwrap(); 524 | /// # let data = [0x76, 0x69, 0x6d, 0x00]; 525 | /// let shmem = IpcSharedMemory::from_bytes(&data); 526 | /// tx.send(shmem.clone()).unwrap(); 527 | /// # let rx_shmem = rx.recv().unwrap(); 528 | /// # assert_eq!(shmem, rx_shmem); 529 | /// ``` 530 | #[derive(Clone, Debug, PartialEq)] 531 | pub struct IpcSharedMemory { 532 | /// None represents no data (empty slice) 533 | os_shared_memory: Option, 534 | } 535 | 536 | impl Deref for IpcSharedMemory { 537 | type Target = [u8]; 538 | 539 | #[inline] 540 | fn deref(&self) -> &[u8] { 541 | if let Some(os_shared_memory) = &self.os_shared_memory { 542 | os_shared_memory 543 | } else { 544 | &[] 545 | } 546 | } 547 | } 548 | 549 | impl IpcSharedMemory { 550 | /// Returns a mutable reference to the deref of this [`IpcSharedMemory`]. 551 | /// 552 | /// # Safety 553 | /// 554 | /// This is safe if there is only one reader/writer on the data. 555 | /// User can achieve this by not cloning [`IpcSharedMemory`] 556 | /// and serializing/deserializing only once. 557 | #[inline] 558 | pub unsafe fn deref_mut(&mut self) -> &mut [u8] { 559 | if let Some(os_shared_memory) = &mut self.os_shared_memory { 560 | os_shared_memory.deref_mut() 561 | } else { 562 | &mut [] 563 | } 564 | } 565 | } 566 | 567 | impl<'de> Deserialize<'de> for IpcSharedMemory { 568 | fn deserialize(deserializer: D) -> Result 569 | where 570 | D: Deserializer<'de>, 571 | { 572 | let index: usize = Deserialize::deserialize(deserializer)?; 573 | if index == usize::MAX { 574 | return Ok(IpcSharedMemory::empty()); 575 | } 576 | 577 | let os_shared_memory = OS_IPC_SHARED_MEMORY_REGIONS_FOR_DESERIALIZATION.with( 578 | |os_ipc_shared_memory_regions_for_deserialization| { 579 | let mut regions = os_ipc_shared_memory_regions_for_deserialization.borrow_mut(); 580 | let Some(region) = regions.get_mut(index) else { 581 | return Err(format!("Cannot consume shared memory region at index {index}, there are only {} regions available", regions.len())); 582 | }; 583 | 584 | region.take().ok_or_else(|| format!("Shared memory region {index} has already been consumed")) 585 | }, 586 | ).map_err(D::Error::custom)?; 587 | 588 | Ok(IpcSharedMemory { 589 | os_shared_memory: Some(os_shared_memory), 590 | }) 591 | } 592 | } 593 | 594 | impl Serialize for IpcSharedMemory { 595 | fn serialize(&self, serializer: S) -> Result 596 | where 597 | S: Serializer, 598 | { 599 | if let Some(os_shared_memory) = &self.os_shared_memory { 600 | let index = OS_IPC_SHARED_MEMORY_REGIONS_FOR_SERIALIZATION.with( 601 | |os_ipc_shared_memory_regions_for_serialization| { 602 | let mut os_ipc_shared_memory_regions_for_serialization = 603 | os_ipc_shared_memory_regions_for_serialization.borrow_mut(); 604 | let index = os_ipc_shared_memory_regions_for_serialization.len(); 605 | os_ipc_shared_memory_regions_for_serialization.push(os_shared_memory.clone()); 606 | index 607 | }, 608 | ); 609 | debug_assert!(index < usize::MAX); 610 | index 611 | } else { 612 | usize::MAX 613 | } 614 | .serialize(serializer) 615 | } 616 | } 617 | 618 | impl IpcSharedMemory { 619 | const fn empty() -> Self { 620 | Self { 621 | os_shared_memory: None, 622 | } 623 | } 624 | 625 | /// Create shared memory initialized with the bytes provided. 626 | pub fn from_bytes(bytes: &[u8]) -> IpcSharedMemory { 627 | if bytes.is_empty() { 628 | IpcSharedMemory::empty() 629 | } else { 630 | IpcSharedMemory { 631 | os_shared_memory: Some(OsIpcSharedMemory::from_bytes(bytes)), 632 | } 633 | } 634 | } 635 | 636 | /// Create a chunk of shared memory that is filled with the byte 637 | /// provided. 638 | pub fn from_byte(byte: u8, length: usize) -> IpcSharedMemory { 639 | if length == 0 { 640 | IpcSharedMemory::empty() 641 | } else { 642 | IpcSharedMemory { 643 | os_shared_memory: Some(OsIpcSharedMemory::from_byte(byte, length)), 644 | } 645 | } 646 | } 647 | } 648 | 649 | /// Result for readable events returned from [IpcReceiverSet::select]. 650 | /// 651 | /// [IpcReceiverSet::select]: struct.IpcReceiverSet.html#method.select 652 | pub enum IpcSelectionResult { 653 | /// A message received from the [`IpcReceiver`] in the [`IpcMessage`] form, 654 | /// identified by the `u64` value. 655 | MessageReceived(u64, IpcMessage), 656 | /// The channel has been closed for the [IpcReceiver] identified by the `u64` value. 657 | /// [IpcReceiver]: struct.IpcReceiver.html 658 | ChannelClosed(u64), 659 | } 660 | 661 | impl IpcSelectionResult { 662 | /// Helper method to move the value out of the [IpcSelectionResult] if it 663 | /// is [MessageReceived]. 664 | /// 665 | /// # Panics 666 | /// 667 | /// If the result is [ChannelClosed] this call will panic. 668 | /// 669 | /// [IpcSelectionResult]: enum.IpcSelectionResult.html 670 | /// [MessageReceived]: enum.IpcSelectionResult.html#variant.MessageReceived 671 | /// [ChannelClosed]: enum.IpcSelectionResult.html#variant.ChannelClosed 672 | pub fn unwrap(self) -> (u64, IpcMessage) { 673 | match self { 674 | IpcSelectionResult::MessageReceived(id, message) => (id, message), 675 | IpcSelectionResult::ChannelClosed(id) => { 676 | panic!("IpcSelectionResult::unwrap(): channel {id} closed") 677 | }, 678 | } 679 | } 680 | } 681 | 682 | /// Structure used to represent a raw message from an [`IpcSender`]. 683 | /// 684 | /// Use the [to] method to deserialize the raw result into the requested type. 685 | /// 686 | /// [to]: #method.to 687 | #[derive(PartialEq)] 688 | pub struct IpcMessage { 689 | pub(crate) data: Vec, 690 | pub(crate) os_ipc_channels: Vec, 691 | pub(crate) os_ipc_shared_memory_regions: Vec, 692 | } 693 | 694 | impl IpcMessage { 695 | /// Create a new [`IpcMessage`] with data and without any [`OsOpaqueIpcChannel`]s and 696 | /// [`OsIpcSharedMemory`] regions. 697 | pub fn from_data(data: Vec) -> Self { 698 | Self { 699 | data, 700 | os_ipc_channels: vec![], 701 | os_ipc_shared_memory_regions: vec![], 702 | } 703 | } 704 | } 705 | 706 | impl Debug for IpcMessage { 707 | fn fmt(&self, formatter: &mut Formatter) -> Result<(), fmt::Error> { 708 | match String::from_utf8(self.data.clone()) { 709 | Ok(string) => string.chars().take(256).collect::().fmt(formatter), 710 | Err(..) => self.data[0..min(self.data.len(), 256)].fmt(formatter), 711 | } 712 | } 713 | } 714 | 715 | impl IpcMessage { 716 | pub(crate) fn new( 717 | data: Vec, 718 | os_ipc_channels: Vec, 719 | os_ipc_shared_memory_regions: Vec, 720 | ) -> IpcMessage { 721 | IpcMessage { 722 | data, 723 | os_ipc_channels, 724 | os_ipc_shared_memory_regions, 725 | } 726 | } 727 | 728 | /// Deserialize the raw data in the contained message into the inferred type. 729 | pub fn to(mut self) -> Result 730 | where 731 | T: for<'de> Deserialize<'de> + Serialize, 732 | { 733 | OS_IPC_CHANNELS_FOR_DESERIALIZATION.with(|os_ipc_channels_for_deserialization| { 734 | OS_IPC_SHARED_MEMORY_REGIONS_FOR_DESERIALIZATION.with( 735 | |os_ipc_shared_memory_regions_for_deserialization| { 736 | mem::swap( 737 | &mut *os_ipc_channels_for_deserialization.borrow_mut(), 738 | &mut self.os_ipc_channels, 739 | ); 740 | let old_ipc_shared_memory_regions_for_deserialization = mem::replace( 741 | &mut *os_ipc_shared_memory_regions_for_deserialization.borrow_mut(), 742 | self.os_ipc_shared_memory_regions 743 | .into_iter() 744 | .map(Some) 745 | .collect(), 746 | ); 747 | let result = bincode::deserialize(&self.data[..]); 748 | *os_ipc_shared_memory_regions_for_deserialization.borrow_mut() = 749 | old_ipc_shared_memory_regions_for_deserialization; 750 | mem::swap( 751 | &mut *os_ipc_channels_for_deserialization.borrow_mut(), 752 | &mut self.os_ipc_channels, 753 | ); 754 | /* Error check comes after doing cleanup, 755 | * since we need the cleanup both in the success and the error cases. */ 756 | result 757 | }, 758 | ) 759 | }) 760 | } 761 | } 762 | 763 | #[derive(Clone, Debug)] 764 | pub struct OpaqueIpcSender { 765 | os_sender: OsIpcSender, 766 | } 767 | 768 | impl OpaqueIpcSender { 769 | pub fn to<'de, T>(self) -> IpcSender 770 | where 771 | T: Deserialize<'de> + Serialize, 772 | { 773 | IpcSender { 774 | os_sender: self.os_sender, 775 | phantom: PhantomData, 776 | } 777 | } 778 | } 779 | 780 | impl<'de> Deserialize<'de> for OpaqueIpcSender { 781 | fn deserialize(deserializer: D) -> Result 782 | where 783 | D: Deserializer<'de>, 784 | { 785 | let os_sender = deserialize_os_ipc_sender(deserializer)?; 786 | Ok(OpaqueIpcSender { os_sender }) 787 | } 788 | } 789 | 790 | impl Serialize for OpaqueIpcSender { 791 | fn serialize(&self, serializer: S) -> Result 792 | where 793 | S: Serializer, 794 | { 795 | serialize_os_ipc_sender(&self.os_sender, serializer) 796 | } 797 | } 798 | 799 | #[derive(Debug)] 800 | pub struct OpaqueIpcReceiver { 801 | os_receiver: OsIpcReceiver, 802 | } 803 | 804 | impl OpaqueIpcReceiver { 805 | pub fn to<'de, T>(self) -> IpcReceiver 806 | where 807 | T: Deserialize<'de> + Serialize, 808 | { 809 | IpcReceiver { 810 | os_receiver: self.os_receiver, 811 | phantom: PhantomData, 812 | } 813 | } 814 | } 815 | 816 | impl<'de> Deserialize<'de> for OpaqueIpcReceiver { 817 | fn deserialize(deserializer: D) -> Result 818 | where 819 | D: Deserializer<'de>, 820 | { 821 | let os_receiver = deserialize_os_ipc_receiver(deserializer)?; 822 | Ok(OpaqueIpcReceiver { os_receiver }) 823 | } 824 | } 825 | 826 | impl Serialize for OpaqueIpcReceiver { 827 | fn serialize(&self, serializer: S) -> Result 828 | where 829 | S: Serializer, 830 | { 831 | serialize_os_ipc_receiver(&self.os_receiver, serializer) 832 | } 833 | } 834 | 835 | /// A server associated with a given name. The server is "one-shot" because 836 | /// it accepts only one connect request from a client. 837 | /// 838 | /// # Examples 839 | /// 840 | /// ## Basic Usage 841 | /// 842 | /// ``` 843 | /// use ipc_channel::ipc::{self, IpcOneShotServer, IpcSender, IpcReceiver}; 844 | /// 845 | /// let (server, server_name) = IpcOneShotServer::new().unwrap(); 846 | /// let tx: IpcSender> = IpcSender::connect(server_name).unwrap(); 847 | /// 848 | /// tx.send(vec![0x10, 0x11, 0x12, 0x13]).unwrap(); 849 | /// let (_, data): (_, Vec) = server.accept().unwrap(); 850 | /// assert_eq!(data, vec![0x10, 0x11, 0x12, 0x13]); 851 | /// ``` 852 | /// 853 | /// ## Sending an [IpcSender] 854 | /// ``` 855 | /// use ipc_channel::ipc::{self, IpcOneShotServer, IpcSender, IpcReceiver}; 856 | /// let (server, name) = IpcOneShotServer::new().unwrap(); 857 | /// 858 | /// let (tx1, rx1): (IpcSender>, IpcReceiver>) = ipc::channel().unwrap(); 859 | /// let tx0 = IpcSender::connect(name).unwrap(); 860 | /// tx0.send(tx1).unwrap(); 861 | /// 862 | /// let (_, tx1): (_, IpcSender>) = server.accept().unwrap(); 863 | /// tx1.send(vec![0x48, 0x65, 0x6b, 0x6b, 0x6f, 0x00]).unwrap(); 864 | /// 865 | /// let data = rx1.recv().unwrap(); 866 | /// assert_eq!(data, vec![0x48, 0x65, 0x6b, 0x6b, 0x6f, 0x00]); 867 | /// ``` 868 | /// [IpcSender]: struct.IpcSender.html 869 | pub struct IpcOneShotServer { 870 | os_server: OsIpcOneShotServer, 871 | phantom: PhantomData, 872 | } 873 | 874 | impl IpcOneShotServer 875 | where 876 | T: for<'de> Deserialize<'de> + Serialize, 877 | { 878 | pub fn new() -> Result<(IpcOneShotServer, String), io::Error> { 879 | let (os_server, name) = OsIpcOneShotServer::new()?; 880 | Ok(( 881 | IpcOneShotServer { 882 | os_server, 883 | phantom: PhantomData, 884 | }, 885 | name, 886 | )) 887 | } 888 | 889 | pub fn accept(self) -> Result<(IpcReceiver, T), bincode::Error> { 890 | let (os_receiver, ipc_message) = self.os_server.accept()?; 891 | Ok(( 892 | IpcReceiver { 893 | os_receiver, 894 | phantom: PhantomData, 895 | }, 896 | ipc_message.to()?, 897 | )) 898 | } 899 | } 900 | 901 | /// Receiving end of a channel that does not used serialized messages. 902 | #[derive(Debug)] 903 | pub struct IpcBytesReceiver { 904 | os_receiver: OsIpcReceiver, 905 | } 906 | 907 | impl IpcBytesReceiver { 908 | /// Blocking receive. 909 | #[inline] 910 | pub fn recv(&self) -> Result, IpcError> { 911 | match self.os_receiver.recv() { 912 | Ok(ipc_message) => Ok(ipc_message.data), 913 | Err(err) => Err(err.into()), 914 | } 915 | } 916 | 917 | /// Non-blocking receive 918 | pub fn try_recv(&self) -> Result, TryRecvError> { 919 | match self.os_receiver.try_recv() { 920 | Ok(ipc_message) => Ok(ipc_message.data), 921 | Err(err) => Err(err.into()), 922 | } 923 | } 924 | } 925 | 926 | impl<'de> Deserialize<'de> for IpcBytesReceiver { 927 | fn deserialize(deserializer: D) -> Result 928 | where 929 | D: Deserializer<'de>, 930 | { 931 | let os_receiver = deserialize_os_ipc_receiver(deserializer)?; 932 | Ok(IpcBytesReceiver { os_receiver }) 933 | } 934 | } 935 | 936 | impl Serialize for IpcBytesReceiver { 937 | fn serialize(&self, serializer: S) -> Result 938 | where 939 | S: Serializer, 940 | { 941 | serialize_os_ipc_receiver(&self.os_receiver, serializer) 942 | } 943 | } 944 | 945 | /// Sending end of a channel that does not used serialized messages. 946 | #[derive(Debug)] 947 | pub struct IpcBytesSender { 948 | os_sender: OsIpcSender, 949 | } 950 | 951 | impl Clone for IpcBytesSender { 952 | fn clone(&self) -> IpcBytesSender { 953 | IpcBytesSender { 954 | os_sender: self.os_sender.clone(), 955 | } 956 | } 957 | } 958 | 959 | impl<'de> Deserialize<'de> for IpcBytesSender { 960 | fn deserialize(deserializer: D) -> Result 961 | where 962 | D: Deserializer<'de>, 963 | { 964 | let os_sender = deserialize_os_ipc_sender(deserializer)?; 965 | Ok(IpcBytesSender { os_sender }) 966 | } 967 | } 968 | 969 | impl Serialize for IpcBytesSender { 970 | fn serialize(&self, serializer: S) -> Result 971 | where 972 | S: Serializer, 973 | { 974 | serialize_os_ipc_sender(&self.os_sender, serializer) 975 | } 976 | } 977 | 978 | impl IpcBytesSender { 979 | #[inline] 980 | pub fn send(&self, data: &[u8]) -> Result<(), io::Error> { 981 | self.os_sender 982 | .send(data, vec![], vec![]) 983 | .map_err(io::Error::from) 984 | } 985 | } 986 | 987 | fn serialize_os_ipc_sender(os_ipc_sender: &OsIpcSender, serializer: S) -> Result 988 | where 989 | S: Serializer, 990 | { 991 | let index = OS_IPC_CHANNELS_FOR_SERIALIZATION.with(|os_ipc_channels_for_serialization| { 992 | let mut os_ipc_channels_for_serialization = os_ipc_channels_for_serialization.borrow_mut(); 993 | let index = os_ipc_channels_for_serialization.len(); 994 | os_ipc_channels_for_serialization.push(OsIpcChannel::Sender(os_ipc_sender.clone())); 995 | index 996 | }); 997 | index.serialize(serializer) 998 | } 999 | 1000 | fn deserialize_os_ipc_sender<'de, D>(deserializer: D) -> Result 1001 | where 1002 | D: Deserializer<'de>, 1003 | { 1004 | let index: usize = Deserialize::deserialize(deserializer)?; 1005 | OS_IPC_CHANNELS_FOR_DESERIALIZATION.with(|os_ipc_channels_for_deserialization| { 1006 | // FIXME(pcwalton): This could panic if the data was corrupt and the index was out of 1007 | // bounds. We should return an `Err` result instead. 1008 | Ok(os_ipc_channels_for_deserialization.borrow_mut()[index].to_sender()) 1009 | }) 1010 | } 1011 | 1012 | fn serialize_os_ipc_receiver( 1013 | os_receiver: &OsIpcReceiver, 1014 | serializer: S, 1015 | ) -> Result 1016 | where 1017 | S: Serializer, 1018 | { 1019 | let index = OS_IPC_CHANNELS_FOR_SERIALIZATION.with(|os_ipc_channels_for_serialization| { 1020 | let mut os_ipc_channels_for_serialization = os_ipc_channels_for_serialization.borrow_mut(); 1021 | let index = os_ipc_channels_for_serialization.len(); 1022 | os_ipc_channels_for_serialization.push(OsIpcChannel::Receiver(os_receiver.consume())); 1023 | index 1024 | }); 1025 | index.serialize(serializer) 1026 | } 1027 | 1028 | fn deserialize_os_ipc_receiver<'de, D>(deserializer: D) -> Result 1029 | where 1030 | D: Deserializer<'de>, 1031 | { 1032 | let index: usize = Deserialize::deserialize(deserializer)?; 1033 | 1034 | OS_IPC_CHANNELS_FOR_DESERIALIZATION.with(|os_ipc_channels_for_deserialization| { 1035 | // FIXME(pcwalton): This could panic if the data was corrupt and the index was out 1036 | // of bounds. We should return an `Err` result instead. 1037 | Ok(os_ipc_channels_for_deserialization.borrow_mut()[index].to_receiver()) 1038 | }) 1039 | } 1040 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Servo Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution. 3 | // 4 | // Licensed under the Apache License, Version 2.0 or the MIT license 6 | // , at your 7 | // option. This file may not be copied, modified, or distributed 8 | // except according to those terms. 9 | 10 | #![doc = include_str!("../README.md")] 11 | //! 12 | //! # Features 13 | //! ## `force-inprocess` 14 | //! 15 | //! Force the `inprocess` backend to be used instead of the OS specific backend. 16 | //! The `inprocess` backend is a dummy back-end, that behaves like the real ones, 17 | //! but doesn't actually work between processes. 18 | 19 | #[cfg(any( 20 | feature = "force-inprocess", 21 | target_os = "windows", 22 | target_os = "android", 23 | target_os = "ios" 24 | ))] 25 | #[cfg(all(not(feature = "force-inprocess"), target_os = "linux"))] 26 | #[cfg(feature = "async")] 27 | use futures; 28 | 29 | #[cfg(feature = "async")] 30 | pub mod asynch; 31 | 32 | #[cfg(all(not(feature = "force-inprocess"), target_os = "windows"))] 33 | extern crate windows; 34 | 35 | pub mod ipc; 36 | pub mod platform; 37 | pub mod router; 38 | 39 | #[cfg(test)] 40 | mod test; 41 | 42 | pub use bincode::{Error, ErrorKind}; 43 | -------------------------------------------------------------------------------- /src/platform/inprocess/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Servo Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution. 3 | // 4 | // Licensed under the Apache License, Version 2.0 or the MIT license 6 | // , at your 7 | // option. This file may not be copied, modified, or distributed 8 | // except according to those terms. 9 | 10 | use crate::ipc::{self, IpcMessage}; 11 | use bincode; 12 | use crossbeam_channel::{self, Receiver, RecvTimeoutError, Select, Sender, TryRecvError}; 13 | use std::cell::{Ref, RefCell}; 14 | use std::cmp::PartialEq; 15 | use std::collections::hash_map::HashMap; 16 | use std::error::Error as StdError; 17 | use std::fmt::{self, Debug, Formatter}; 18 | use std::io; 19 | use std::ops::{Deref, RangeFrom}; 20 | use std::ptr::eq; 21 | use std::slice; 22 | use std::sync::{Arc, LazyLock, Mutex}; 23 | use std::time::Duration; 24 | use uuid::Uuid; 25 | 26 | #[derive(Clone)] 27 | struct ServerRecord { 28 | sender: OsIpcSender, 29 | conn_sender: Sender, 30 | conn_receiver: Receiver, 31 | } 32 | 33 | impl ServerRecord { 34 | fn new(sender: OsIpcSender) -> ServerRecord { 35 | let (tx, rx) = crossbeam_channel::unbounded::(); 36 | ServerRecord { 37 | sender, 38 | conn_sender: tx, 39 | conn_receiver: rx, 40 | } 41 | } 42 | 43 | fn accept(&self) { 44 | self.conn_receiver.recv().unwrap(); 45 | } 46 | 47 | fn connect(&self) { 48 | self.conn_sender.send(true).unwrap(); 49 | } 50 | } 51 | 52 | static ONE_SHOT_SERVERS: LazyLock>> = 53 | LazyLock::new(|| Mutex::new(HashMap::new())); 54 | 55 | struct ChannelMessage(IpcMessage); 56 | 57 | pub fn channel() -> Result<(OsIpcSender, OsIpcReceiver), ChannelError> { 58 | let (base_sender, base_receiver) = crossbeam_channel::unbounded::(); 59 | Ok(( 60 | OsIpcSender::new(base_sender), 61 | OsIpcReceiver::new(base_receiver), 62 | )) 63 | } 64 | 65 | #[derive(Debug)] 66 | pub struct OsIpcReceiver { 67 | receiver: RefCell>>, 68 | } 69 | 70 | impl PartialEq for OsIpcReceiver { 71 | fn eq(&self, other: &OsIpcReceiver) -> bool { 72 | self.receiver.borrow().as_ref().map(|rx| rx as *const _) 73 | == other.receiver.borrow().as_ref().map(|rx| rx as *const _) 74 | } 75 | } 76 | 77 | impl OsIpcReceiver { 78 | fn new(receiver: Receiver) -> OsIpcReceiver { 79 | OsIpcReceiver { 80 | receiver: RefCell::new(Some(receiver)), 81 | } 82 | } 83 | 84 | pub fn consume(&self) -> OsIpcReceiver { 85 | OsIpcReceiver { 86 | receiver: RefCell::new(self.receiver.borrow_mut().take()), 87 | } 88 | } 89 | 90 | pub fn recv(&self) -> Result { 91 | let r = self.receiver.borrow(); 92 | let r = r.as_ref().unwrap(); 93 | match r.recv() { 94 | Ok(ChannelMessage(ipc_message)) => Ok(ipc_message), 95 | Err(_) => Err(ChannelError::ChannelClosedError), 96 | } 97 | } 98 | 99 | pub fn try_recv(&self) -> Result { 100 | let r = self.receiver.borrow(); 101 | let r = r.as_ref().unwrap(); 102 | match r.try_recv() { 103 | Ok(ChannelMessage(ipc_message)) => Ok(ipc_message), 104 | Err(e) => match e { 105 | TryRecvError::Empty => Err(ChannelError::ChannelEmpty), 106 | TryRecvError::Disconnected => Err(ChannelError::ChannelClosedError), 107 | }, 108 | } 109 | } 110 | 111 | pub fn try_recv_timeout(&self, duration: Duration) -> Result { 112 | let r = self.receiver.borrow(); 113 | let r = r.as_ref().unwrap(); 114 | match r.recv_timeout(duration) { 115 | Ok(ChannelMessage(ipc_message)) => Ok(ipc_message), 116 | Err(e) => match e { 117 | RecvTimeoutError::Timeout => Err(ChannelError::ChannelEmpty), 118 | RecvTimeoutError::Disconnected => Err(ChannelError::ChannelClosedError), 119 | }, 120 | } 121 | } 122 | } 123 | 124 | #[derive(Clone, Debug)] 125 | pub struct OsIpcSender { 126 | sender: Sender, 127 | } 128 | 129 | impl PartialEq for OsIpcSender { 130 | fn eq(&self, other: &OsIpcSender) -> bool { 131 | // FIXME: this implementation is wrong: https://github.com/servo/ipc-channel/issues/414 132 | eq(&self.sender as *const _, &other.sender as *const _) 133 | } 134 | } 135 | 136 | impl OsIpcSender { 137 | fn new(sender: Sender) -> OsIpcSender { 138 | OsIpcSender { sender } 139 | } 140 | 141 | pub fn connect(name: String) -> Result { 142 | let record = ONE_SHOT_SERVERS.lock().unwrap().get(&name).unwrap().clone(); 143 | record.connect(); 144 | Ok(record.sender) 145 | } 146 | 147 | pub fn get_max_fragment_size() -> usize { 148 | usize::MAX 149 | } 150 | 151 | pub fn send( 152 | &self, 153 | data: &[u8], 154 | ports: Vec, 155 | shared_memory_regions: Vec, 156 | ) -> Result<(), ChannelError> { 157 | let os_ipc_channels = ports.into_iter().map(OsOpaqueIpcChannel::new).collect(); 158 | let ipc_message = IpcMessage::new(data.to_vec(), os_ipc_channels, shared_memory_regions); 159 | self.sender 160 | .send(ChannelMessage(ipc_message)) 161 | .map_err(|_| ChannelError::BrokenPipeError) 162 | } 163 | } 164 | 165 | pub struct OsIpcReceiverSet { 166 | incrementor: RangeFrom, 167 | receiver_ids: Vec, 168 | receivers: Vec, 169 | } 170 | 171 | impl OsIpcReceiverSet { 172 | pub fn new() -> Result { 173 | Ok(OsIpcReceiverSet { 174 | incrementor: 0.., 175 | receiver_ids: vec![], 176 | receivers: vec![], 177 | }) 178 | } 179 | 180 | pub fn add(&mut self, receiver: OsIpcReceiver) -> Result { 181 | let last_index = self.incrementor.next().unwrap(); 182 | self.receiver_ids.push(last_index); 183 | self.receivers.push(receiver.consume()); 184 | Ok(last_index) 185 | } 186 | 187 | pub fn select(&mut self) -> Result, ChannelError> { 188 | if self.receivers.is_empty() { 189 | return Err(ChannelError::UnknownError); 190 | } 191 | 192 | struct Remove(usize, u64); 193 | 194 | // FIXME: Remove early returns and explicitly drop `borrows` when lifetimes are non-lexical 195 | let Remove(r_index, r_id) = { 196 | let borrows: Vec<_> = self 197 | .receivers 198 | .iter() 199 | .map(|r| Ref::map(r.receiver.borrow(), |o| o.as_ref().unwrap())) 200 | .collect(); 201 | 202 | let mut select = Select::new(); 203 | for r in &borrows { 204 | select.recv(r); 205 | } 206 | let res = select.select(); 207 | let receiver_index = res.index(); 208 | let receiver_id = self.receiver_ids[receiver_index]; 209 | if let Ok(ChannelMessage(ipc_message)) = res.recv(&borrows[receiver_index]) { 210 | return Ok(vec![OsIpcSelectionResult::DataReceived( 211 | receiver_id, 212 | ipc_message, 213 | )]); 214 | } else { 215 | Remove(receiver_index, receiver_id) 216 | } 217 | }; 218 | self.receivers.remove(r_index); 219 | self.receiver_ids.remove(r_index); 220 | Ok(vec![OsIpcSelectionResult::ChannelClosed(r_id)]) 221 | } 222 | } 223 | 224 | pub enum OsIpcSelectionResult { 225 | DataReceived(u64, IpcMessage), 226 | ChannelClosed(u64), 227 | } 228 | 229 | impl OsIpcSelectionResult { 230 | pub fn unwrap(self) -> (u64, IpcMessage) { 231 | match self { 232 | OsIpcSelectionResult::DataReceived(id, ipc_message) => (id, ipc_message), 233 | OsIpcSelectionResult::ChannelClosed(id) => { 234 | panic!("OsIpcSelectionResult::unwrap(): receiver ID {id} was closed!") 235 | }, 236 | } 237 | } 238 | } 239 | 240 | pub struct OsIpcOneShotServer { 241 | receiver: OsIpcReceiver, 242 | name: String, 243 | } 244 | 245 | impl OsIpcOneShotServer { 246 | pub fn new() -> Result<(OsIpcOneShotServer, String), ChannelError> { 247 | let (sender, receiver) = channel()?; 248 | 249 | let name = Uuid::new_v4().to_string(); 250 | let record = ServerRecord::new(sender); 251 | ONE_SHOT_SERVERS 252 | .lock() 253 | .unwrap() 254 | .insert(name.clone(), record); 255 | Ok(( 256 | OsIpcOneShotServer { 257 | receiver, 258 | name: name.clone(), 259 | }, 260 | name.clone(), 261 | )) 262 | } 263 | 264 | pub fn accept(self) -> Result<(OsIpcReceiver, IpcMessage), ChannelError> { 265 | let record = ONE_SHOT_SERVERS 266 | .lock() 267 | .unwrap() 268 | .get(&self.name) 269 | .unwrap() 270 | .clone(); 271 | record.accept(); 272 | ONE_SHOT_SERVERS.lock().unwrap().remove(&self.name).unwrap(); 273 | let ipc_message = self.receiver.recv()?; 274 | Ok((self.receiver, ipc_message)) 275 | } 276 | } 277 | 278 | #[derive(PartialEq, Debug)] 279 | pub enum OsIpcChannel { 280 | Sender(OsIpcSender), 281 | Receiver(OsIpcReceiver), 282 | } 283 | 284 | #[derive(PartialEq, Debug)] 285 | pub struct OsOpaqueIpcChannel { 286 | channel: RefCell>, 287 | } 288 | 289 | impl OsOpaqueIpcChannel { 290 | fn new(channel: OsIpcChannel) -> OsOpaqueIpcChannel { 291 | OsOpaqueIpcChannel { 292 | channel: RefCell::new(Some(channel)), 293 | } 294 | } 295 | 296 | pub fn to_receiver(&self) -> OsIpcReceiver { 297 | match self.channel.borrow_mut().take().unwrap() { 298 | OsIpcChannel::Sender(_) => panic!("Opaque channel is not a receiver!"), 299 | OsIpcChannel::Receiver(r) => r, 300 | } 301 | } 302 | 303 | pub fn to_sender(&mut self) -> OsIpcSender { 304 | match self.channel.borrow_mut().take().unwrap() { 305 | OsIpcChannel::Sender(s) => s, 306 | OsIpcChannel::Receiver(_) => panic!("Opaque channel is not a sender!"), 307 | } 308 | } 309 | } 310 | 311 | pub struct OsIpcSharedMemory { 312 | ptr: *mut u8, 313 | length: usize, 314 | data: Arc>, 315 | } 316 | 317 | unsafe impl Send for OsIpcSharedMemory {} 318 | unsafe impl Sync for OsIpcSharedMemory {} 319 | 320 | impl Clone for OsIpcSharedMemory { 321 | fn clone(&self) -> OsIpcSharedMemory { 322 | OsIpcSharedMemory { 323 | ptr: self.ptr, 324 | length: self.length, 325 | data: self.data.clone(), 326 | } 327 | } 328 | } 329 | 330 | impl PartialEq for OsIpcSharedMemory { 331 | fn eq(&self, other: &OsIpcSharedMemory) -> bool { 332 | **self == **other 333 | } 334 | } 335 | 336 | impl Debug for OsIpcSharedMemory { 337 | fn fmt(&self, formatter: &mut Formatter) -> Result<(), fmt::Error> { 338 | (**self).fmt(formatter) 339 | } 340 | } 341 | 342 | impl Deref for OsIpcSharedMemory { 343 | type Target = [u8]; 344 | 345 | #[inline] 346 | fn deref(&self) -> &[u8] { 347 | if self.ptr.is_null() { 348 | panic!("attempted to access a consumed `OsIpcSharedMemory`") 349 | } 350 | unsafe { slice::from_raw_parts(self.ptr, self.length) } 351 | } 352 | } 353 | 354 | impl OsIpcSharedMemory { 355 | /// # Safety 356 | /// 357 | /// This is safe if there is only one reader/writer on the data. 358 | /// User can achieve this by not cloning [`IpcSharedMemory`] 359 | /// and serializing/deserializing only once. 360 | #[inline] 361 | pub unsafe fn deref_mut(&mut self) -> &mut [u8] { 362 | if self.ptr.is_null() { 363 | panic!("attempted to access a consumed `OsIpcSharedMemory`") 364 | } 365 | unsafe { slice::from_raw_parts_mut(self.ptr, self.length) } 366 | } 367 | } 368 | 369 | impl OsIpcSharedMemory { 370 | pub fn from_byte(byte: u8, length: usize) -> OsIpcSharedMemory { 371 | let mut v = Arc::new(vec![byte; length]); 372 | OsIpcSharedMemory { 373 | ptr: Arc::get_mut(&mut v).unwrap().as_mut_ptr(), 374 | length, 375 | data: v, 376 | } 377 | } 378 | 379 | pub fn from_bytes(bytes: &[u8]) -> OsIpcSharedMemory { 380 | let mut v = Arc::new(bytes.to_vec()); 381 | OsIpcSharedMemory { 382 | ptr: Arc::get_mut(&mut v).unwrap().as_mut_ptr(), 383 | length: v.len(), 384 | data: v, 385 | } 386 | } 387 | } 388 | 389 | #[derive(Debug, PartialEq)] 390 | pub enum ChannelError { 391 | ChannelClosedError, 392 | BrokenPipeError, 393 | ChannelEmpty, 394 | UnknownError, 395 | } 396 | 397 | impl ChannelError { 398 | #[allow(dead_code)] 399 | pub fn channel_is_closed(&self) -> bool { 400 | *self == ChannelError::ChannelClosedError 401 | } 402 | } 403 | 404 | impl fmt::Display for ChannelError { 405 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 406 | match *self { 407 | ChannelError::ChannelClosedError => write!(fmt, "channel closed"), 408 | ChannelError::BrokenPipeError => write!(fmt, "broken pipe"), 409 | ChannelError::ChannelEmpty => write!(fmt, "channel empty"), 410 | ChannelError::UnknownError => write!(fmt, "unknown error"), 411 | } 412 | } 413 | } 414 | 415 | impl StdError for ChannelError {} 416 | 417 | impl From for bincode::Error { 418 | fn from(crossbeam_error: ChannelError) -> Self { 419 | io::Error::from(crossbeam_error).into() 420 | } 421 | } 422 | 423 | impl From for ipc::IpcError { 424 | fn from(error: ChannelError) -> Self { 425 | match error { 426 | ChannelError::ChannelClosedError => ipc::IpcError::Disconnected, 427 | e => ipc::IpcError::Bincode(io::Error::from(e).into()), 428 | } 429 | } 430 | } 431 | 432 | impl From for ipc::TryRecvError { 433 | fn from(error: ChannelError) -> Self { 434 | match error { 435 | ChannelError::ChannelClosedError => { 436 | ipc::TryRecvError::IpcError(ipc::IpcError::Disconnected) 437 | }, 438 | ChannelError::ChannelEmpty => ipc::TryRecvError::Empty, 439 | e => ipc::TryRecvError::IpcError(ipc::IpcError::Bincode(io::Error::from(e).into())), 440 | } 441 | } 442 | } 443 | 444 | impl From for io::Error { 445 | fn from(crossbeam_error: ChannelError) -> io::Error { 446 | match crossbeam_error { 447 | ChannelError::ChannelClosedError => io::Error::new( 448 | io::ErrorKind::ConnectionReset, 449 | "crossbeam-channel sender closed", 450 | ), 451 | ChannelError::ChannelEmpty => io::Error::new( 452 | io::ErrorKind::ConnectionReset, 453 | "crossbeam-channel receiver has no received messages", 454 | ), 455 | ChannelError::BrokenPipeError => io::Error::new( 456 | io::ErrorKind::BrokenPipe, 457 | "crossbeam-channel receiver closed", 458 | ), 459 | ChannelError::UnknownError => io::Error::other("Other crossbeam-channel error"), 460 | } 461 | } 462 | } 463 | -------------------------------------------------------------------------------- /src/platform/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Servo Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution. 3 | // 4 | // Licensed under the Apache License, Version 2.0 or the MIT license 6 | // , at your 7 | // option. This file may not be copied, modified, or distributed 8 | // except according to those terms. 9 | 10 | #[cfg(all( 11 | not(feature = "force-inprocess"), 12 | any( 13 | target_os = "linux", 14 | target_os = "openbsd", 15 | target_os = "freebsd", 16 | target_os = "illumos", 17 | ) 18 | ))] 19 | mod unix; 20 | #[cfg(all( 21 | not(feature = "force-inprocess"), 22 | any( 23 | target_os = "linux", 24 | target_os = "openbsd", 25 | target_os = "freebsd", 26 | target_os = "illumos", 27 | ) 28 | ))] 29 | mod os { 30 | pub use super::unix::*; 31 | } 32 | 33 | #[cfg(all(not(feature = "force-inprocess"), target_os = "macos"))] 34 | mod macos; 35 | #[cfg(all(not(feature = "force-inprocess"), target_os = "macos"))] 36 | mod os { 37 | pub use super::macos::*; 38 | } 39 | 40 | #[cfg(all(not(feature = "force-inprocess"), target_os = "windows"))] 41 | mod windows; 42 | #[cfg(all(not(feature = "force-inprocess"), target_os = "windows"))] 43 | mod os { 44 | pub use super::windows::*; 45 | } 46 | 47 | #[cfg(any( 48 | feature = "force-inprocess", 49 | target_os = "android", 50 | target_os = "ios", 51 | target_os = "wasi", 52 | target_os = "unknown" 53 | ))] 54 | mod inprocess; 55 | #[cfg(any( 56 | feature = "force-inprocess", 57 | target_os = "android", 58 | target_os = "ios", 59 | target_os = "wasi", 60 | target_os = "unknown" 61 | ))] 62 | mod os { 63 | pub use super::inprocess::*; 64 | } 65 | 66 | pub use self::os::{channel, OsOpaqueIpcChannel}; 67 | pub use self::os::{OsIpcChannel, OsIpcOneShotServer, OsIpcReceiver, OsIpcReceiverSet}; 68 | pub use self::os::{OsIpcSelectionResult, OsIpcSender, OsIpcSharedMemory}; 69 | 70 | #[cfg(test)] 71 | mod test; 72 | -------------------------------------------------------------------------------- /src/platform/unix/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Servo Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution. 3 | // 4 | // Licensed under the Apache License, Version 2.0 or the MIT license 6 | // , at your 7 | // option. This file may not be copied, modified, or distributed 8 | // except according to those terms. 9 | 10 | use crate::ipc::{self, IpcMessage}; 11 | use bincode; 12 | use fnv::FnvHasher; 13 | use libc::{ 14 | self, cmsghdr, linger, CMSG_DATA, CMSG_LEN, CMSG_SPACE, MAP_FAILED, MAP_SHARED, PROT_READ, 15 | PROT_WRITE, SOCK_SEQPACKET, SOL_SOCKET, 16 | }; 17 | use libc::{c_char, c_int, c_void, getsockopt, SO_LINGER, S_IFMT, S_IFSOCK}; 18 | use libc::{iovec, msghdr, off_t, recvmsg, sendmsg}; 19 | use libc::{sa_family_t, setsockopt, size_t, sockaddr, sockaddr_un, socketpair, socklen_t}; 20 | use libc::{EAGAIN, EWOULDBLOCK}; 21 | use mio::unix::SourceFd; 22 | use mio::{Events, Interest, Poll, Token}; 23 | use std::cell::Cell; 24 | use std::cmp; 25 | use std::collections::HashMap; 26 | use std::convert::TryInto; 27 | use std::error::Error as StdError; 28 | use std::ffi::{c_uint, CString}; 29 | use std::fmt::{self, Debug, Formatter}; 30 | use std::hash::BuildHasherDefault; 31 | use std::io; 32 | use std::mem; 33 | use std::ops::{Deref, RangeFrom}; 34 | use std::os::fd::RawFd; 35 | use std::ptr; 36 | use std::slice; 37 | use std::sync::atomic::{AtomicUsize, Ordering}; 38 | use std::sync::{Arc, LazyLock}; 39 | use std::thread; 40 | use std::time::{Duration, UNIX_EPOCH}; 41 | use tempfile::{Builder, TempDir}; 42 | 43 | const MAX_FDS_IN_CMSG: u32 = 64; 44 | 45 | // The value Linux returns for SO_SNDBUF 46 | // is not the size we are actually allowed to use... 47 | // Empirically, we have to deduct 32 bytes from that. 48 | const RESERVED_SIZE: usize = 32; 49 | 50 | #[cfg(any(target_os = "linux", target_os = "illumos"))] 51 | const SOCK_FLAGS: c_int = libc::SOCK_CLOEXEC; 52 | #[cfg(not(any(target_os = "linux", target_os = "illumos")))] 53 | const SOCK_FLAGS: c_int = 0; 54 | 55 | #[cfg(any(target_os = "linux", target_os = "illumos"))] 56 | const RECVMSG_FLAGS: c_int = libc::MSG_CMSG_CLOEXEC; 57 | #[cfg(not(any(target_os = "linux", target_os = "illumos")))] 58 | const RECVMSG_FLAGS: c_int = 0; 59 | 60 | #[cfg(target_env = "gnu")] 61 | type IovLen = usize; 62 | #[cfg(target_env = "gnu")] 63 | type MsgControlLen = size_t; 64 | 65 | #[cfg(not(target_env = "gnu"))] 66 | type IovLen = i32; 67 | #[cfg(not(target_env = "gnu"))] 68 | type MsgControlLen = socklen_t; 69 | 70 | unsafe fn new_sockaddr_un(path: *const c_char) -> (sockaddr_un, usize) { 71 | let mut sockaddr: sockaddr_un = mem::zeroed(); 72 | libc::strncpy( 73 | sockaddr.sun_path.as_mut_ptr(), 74 | path, 75 | sockaddr.sun_path.len() - 1, 76 | ); 77 | sockaddr.sun_family = libc::AF_UNIX as sa_family_t; 78 | (sockaddr, mem::size_of::()) 79 | } 80 | 81 | static SYSTEM_SENDBUF_SIZE: LazyLock = LazyLock::new(|| { 82 | let (tx, _) = channel().expect("Failed to obtain a socket for checking maximum send size"); 83 | tx.get_system_sendbuf_size() 84 | .expect("Failed to obtain maximum send size for socket") 85 | }); 86 | 87 | // The pid of the current process which is used to create unique IDs 88 | static PID: LazyLock = LazyLock::new(std::process::id); 89 | 90 | // A global count used to create unique IDs 91 | static SHM_COUNT: AtomicUsize = AtomicUsize::new(0); 92 | 93 | pub fn channel() -> Result<(OsIpcSender, OsIpcReceiver), UnixError> { 94 | let mut results = [0, 0]; 95 | unsafe { 96 | if socketpair( 97 | libc::AF_UNIX, 98 | SOCK_SEQPACKET | SOCK_FLAGS, 99 | 0, 100 | &mut results[0], 101 | ) >= 0 102 | { 103 | Ok(( 104 | OsIpcSender::from_fd(results[0]), 105 | OsIpcReceiver::from_fd(results[1]), 106 | )) 107 | } else { 108 | Err(UnixError::last()) 109 | } 110 | } 111 | } 112 | 113 | #[derive(Clone, Copy)] 114 | struct PollEntry { 115 | pub id: u64, 116 | pub fd: RawFd, 117 | } 118 | 119 | #[derive(PartialEq, Debug)] 120 | pub struct OsIpcReceiver { 121 | fd: Cell, 122 | } 123 | 124 | impl Drop for OsIpcReceiver { 125 | fn drop(&mut self) { 126 | unsafe { 127 | let fd = self.fd.get(); 128 | if fd >= 0 { 129 | let result = libc::close(fd); 130 | assert!( 131 | thread::panicking() || result == 0, 132 | "closed receiver (fd: {}): {}", 133 | fd, 134 | UnixError::last(), 135 | ); 136 | } 137 | } 138 | } 139 | } 140 | 141 | impl OsIpcReceiver { 142 | fn from_fd(fd: c_int) -> OsIpcReceiver { 143 | OsIpcReceiver { fd: Cell::new(fd) } 144 | } 145 | 146 | fn consume_fd(&self) -> c_int { 147 | let fd = self.fd.get(); 148 | self.fd.set(-1); 149 | fd 150 | } 151 | 152 | pub fn consume(&self) -> OsIpcReceiver { 153 | OsIpcReceiver::from_fd(self.consume_fd()) 154 | } 155 | 156 | #[allow(clippy::type_complexity)] 157 | pub fn recv(&self) -> Result { 158 | recv(self.fd.get(), BlockingMode::Blocking) 159 | } 160 | 161 | #[allow(clippy::type_complexity)] 162 | pub fn try_recv(&self) -> Result { 163 | recv(self.fd.get(), BlockingMode::Nonblocking) 164 | } 165 | 166 | #[allow(clippy::type_complexity)] 167 | pub fn try_recv_timeout(&self, duration: Duration) -> Result { 168 | recv(self.fd.get(), BlockingMode::Timeout(duration)) 169 | } 170 | } 171 | 172 | #[derive(PartialEq, Debug)] 173 | struct SharedFileDescriptor(c_int); 174 | 175 | impl Drop for SharedFileDescriptor { 176 | fn drop(&mut self) { 177 | unsafe { 178 | let result = libc::close(self.0); 179 | assert!(thread::panicking() || result == 0); 180 | } 181 | } 182 | } 183 | 184 | #[derive(PartialEq, Debug, Clone)] 185 | pub struct OsIpcSender { 186 | fd: Arc, 187 | } 188 | 189 | impl OsIpcSender { 190 | fn from_fd(fd: c_int) -> OsIpcSender { 191 | OsIpcSender { 192 | fd: Arc::new(SharedFileDescriptor(fd)), 193 | } 194 | } 195 | 196 | /// Maximum size of the kernel buffer used for transfers over this channel. 197 | /// 198 | /// Note: This is *not* the actual maximal packet size we are allowed to use... 199 | /// Some of it is reserved by the kernel for bookkeeping. 200 | fn get_system_sendbuf_size(&self) -> Result { 201 | unsafe { 202 | let mut socket_sendbuf_size: c_int = 0; 203 | let mut socket_sendbuf_size_len = mem::size_of::() as socklen_t; 204 | if getsockopt( 205 | self.fd.0, 206 | libc::SOL_SOCKET, 207 | libc::SO_SNDBUF, 208 | &mut socket_sendbuf_size as *mut _ as *mut c_void, 209 | &mut socket_sendbuf_size_len as *mut socklen_t, 210 | ) < 0 211 | { 212 | return Err(UnixError::last()); 213 | } 214 | Ok(socket_sendbuf_size.try_into().unwrap()) 215 | } 216 | } 217 | 218 | /// Calculate maximum payload data size per fragment. 219 | /// 220 | /// It is the total size of the kernel buffer, minus the part reserved by the kernel. 221 | /// 222 | /// The `sendbuf_size` passed in should usually be the maximum kernel buffer size, 223 | /// i.e. the value of *SYSTEM_SENDBUF_SIZE -- 224 | /// except after getting ENOBUFS, in which case it needs to be reduced. 225 | fn fragment_size(sendbuf_size: usize) -> usize { 226 | sendbuf_size - RESERVED_SIZE 227 | } 228 | 229 | /// Calculate maximum payload data size of first fragment. 230 | /// 231 | /// This one is smaller than regular fragments, because it carries the message (size) header. 232 | fn first_fragment_size(sendbuf_size: usize) -> usize { 233 | (Self::fragment_size(sendbuf_size) - mem::size_of::()) & (!8usize + 1) 234 | // Ensure optimal alignment. 235 | } 236 | 237 | /// Maximum data size that can be transferred over this channel in a single packet. 238 | /// 239 | /// This is the size of the main data chunk only -- 240 | /// it's independent of any auxiliary data (FDs) transferred along with it. 241 | /// 242 | /// A send on this channel won't block for transfers up to this size 243 | /// under normal circumstances. 244 | /// (It might still block if heavy memory pressure causes ENOBUFS, 245 | /// forcing us to reduce the packet size.) 246 | pub fn get_max_fragment_size() -> usize { 247 | Self::first_fragment_size(*SYSTEM_SENDBUF_SIZE) 248 | } 249 | 250 | pub fn send( 251 | &self, 252 | data: &[u8], 253 | channels: Vec, 254 | shared_memory_regions: Vec, 255 | ) -> Result<(), UnixError> { 256 | let mut fds = Vec::new(); 257 | for channel in channels.iter() { 258 | fds.push(channel.fd()); 259 | } 260 | for shared_memory_region in shared_memory_regions.iter() { 261 | fds.push(shared_memory_region.store.fd()); 262 | } 263 | 264 | // `len` is the total length of the message. 265 | // Its value will be sent as a message header before the payload data. 266 | // 267 | // Not to be confused with the length of the data to send in this packet 268 | // (i.e. the length of the data buffer passed in), 269 | // which in a fragmented send will be smaller than the total message length. 270 | fn send_first_fragment( 271 | sender_fd: c_int, 272 | fds: &[c_int], 273 | data_buffer: &[u8], 274 | len: usize, 275 | ) -> Result<(), UnixError> { 276 | let result = unsafe { 277 | let cmsg_length = mem::size_of_val(fds) as c_uint; 278 | let (cmsg_buffer, cmsg_space) = if cmsg_length > 0 { 279 | let cmsg_buffer = 280 | libc::malloc(CMSG_SPACE(cmsg_length) as usize) as *mut cmsghdr; 281 | if cmsg_buffer.is_null() { 282 | return Err(UnixError::last()); 283 | } 284 | (*cmsg_buffer).cmsg_len = CMSG_LEN(cmsg_length) as MsgControlLen; 285 | (*cmsg_buffer).cmsg_level = libc::SOL_SOCKET; 286 | (*cmsg_buffer).cmsg_type = libc::SCM_RIGHTS; 287 | 288 | ptr::copy_nonoverlapping( 289 | fds.as_ptr(), 290 | CMSG_DATA(cmsg_buffer) as *mut c_int, 291 | fds.len(), 292 | ); 293 | (cmsg_buffer, CMSG_SPACE(cmsg_length)) 294 | } else { 295 | (ptr::null_mut(), 0) 296 | }; 297 | 298 | let mut iovec = [ 299 | // First fragment begins with a header recording the total data length. 300 | // 301 | // The receiver uses this to determine 302 | // whether it already got the entire message, 303 | // or needs to receive additional fragments -- and if so, how much. 304 | iovec { 305 | iov_base: &len as *const _ as *mut c_void, 306 | iov_len: mem::size_of_val(&len), 307 | }, 308 | iovec { 309 | iov_base: data_buffer.as_ptr() as *mut c_void, 310 | iov_len: data_buffer.len(), 311 | }, 312 | ]; 313 | 314 | let msghdr = new_msghdr(&mut iovec, cmsg_buffer, cmsg_space as MsgControlLen); 315 | let result = sendmsg(sender_fd, &msghdr, 0); 316 | libc::free(cmsg_buffer as *mut c_void); 317 | result 318 | }; 319 | 320 | if result > 0 { 321 | Ok(()) 322 | } else { 323 | Err(UnixError::last()) 324 | } 325 | } 326 | 327 | fn send_followup_fragment(sender_fd: c_int, data_buffer: &[u8]) -> Result<(), UnixError> { 328 | let result = unsafe { 329 | libc::send( 330 | sender_fd, 331 | data_buffer.as_ptr() as *const c_void, 332 | data_buffer.len(), 333 | 0, 334 | ) 335 | }; 336 | 337 | if result > 0 { 338 | Ok(()) 339 | } else { 340 | Err(UnixError::last()) 341 | } 342 | } 343 | 344 | let mut sendbuf_size = *SYSTEM_SENDBUF_SIZE; 345 | 346 | /// Reduce send buffer size after getting ENOBUFS, 347 | /// i.e. when the kernel failed to allocate a large enough buffer. 348 | /// 349 | /// (If the buffer already was significantly smaller 350 | /// than the memory page size though, 351 | /// if means something else must have gone wrong; 352 | /// so there is no point in further downsizing, 353 | /// and we error out instead.) 354 | fn downsize(sendbuf_size: &mut usize, sent_size: usize) -> Result<(), ()> { 355 | if sent_size > 2000 { 356 | *sendbuf_size /= 2; 357 | // Make certain we end up with less than what we tried before... 358 | if *sendbuf_size >= sent_size { 359 | *sendbuf_size = sent_size / 2; 360 | } 361 | Ok(()) 362 | } else { 363 | Err(()) 364 | } 365 | } 366 | 367 | // If the message is small enough, try sending it in a single fragment. 368 | if data.len() <= Self::get_max_fragment_size() { 369 | match send_first_fragment(self.fd.0, &fds[..], data, data.len()) { 370 | Ok(_) => return Ok(()), 371 | Err(error) => { 372 | // ENOBUFS means the kernel failed to allocate a buffer large enough 373 | // to actually transfer the message, 374 | // although the message was small enough to fit the maximum send size -- 375 | // so we have to proceed with a fragmented send nevertheless, 376 | // using a reduced send buffer size. 377 | // 378 | // Any other errors we might get here are non-recoverable. 379 | if !(matches!(error, UnixError::Errno(libc::ENOBUFS)) 380 | && downsize(&mut sendbuf_size, data.len()).is_ok()) 381 | { 382 | return Err(error); 383 | } 384 | }, 385 | } 386 | } 387 | 388 | // The packet is too big. Fragmentation time! 389 | // 390 | // Create dedicated channel to send all but the first fragment. 391 | // This way we avoid fragments of different messages interleaving in the receiver. 392 | // 393 | // The receiver end of the channel is sent with the first fragment 394 | // along any other file descriptors that are to be transferred in the message. 395 | let (dedicated_tx, dedicated_rx) = channel()?; 396 | // Extract FD handle without consuming the Receiver, so the FD doesn't get closed. 397 | fds.push(dedicated_rx.fd.get()); 398 | 399 | // Split up the packet into fragments. 400 | let mut byte_position = 0; 401 | while byte_position < data.len() { 402 | let end_byte_position; 403 | let result = if byte_position == 0 { 404 | // First fragment. No offset; but contains message header (total size). 405 | // The auxiliary data (FDs) is also sent along with this one. 406 | 407 | // This fragment always uses the full allowable buffer size. 408 | end_byte_position = Self::first_fragment_size(sendbuf_size); 409 | send_first_fragment(self.fd.0, &fds[..], &data[..end_byte_position], data.len()) 410 | } else { 411 | // Followup fragment. No header; but offset by amount of data already sent. 412 | 413 | end_byte_position = cmp::min( 414 | byte_position + Self::fragment_size(sendbuf_size), 415 | data.len(), 416 | ); 417 | send_followup_fragment(dedicated_tx.fd.0, &data[byte_position..end_byte_position]) 418 | }; 419 | 420 | if let Err(error) = result { 421 | if matches!(error, UnixError::Errno(libc::ENOBUFS)) 422 | && downsize(&mut sendbuf_size, end_byte_position - byte_position).is_ok() 423 | { 424 | // If the kernel failed to allocate a buffer large enough for the packet, 425 | // retry with a smaller size (if possible). 426 | continue; 427 | } else { 428 | return Err(error); 429 | } 430 | } 431 | 432 | byte_position = end_byte_position; 433 | } 434 | 435 | Ok(()) 436 | } 437 | 438 | pub fn connect(name: String) -> Result { 439 | let name = CString::new(name).unwrap(); 440 | unsafe { 441 | let fd = libc::socket(libc::AF_UNIX, SOCK_SEQPACKET | SOCK_FLAGS, 0); 442 | let (sockaddr, len) = new_sockaddr_un(name.as_ptr()); 443 | if libc::connect( 444 | fd, 445 | &sockaddr as *const _ as *const sockaddr, 446 | len as socklen_t, 447 | ) < 0 448 | { 449 | return Err(UnixError::last()); 450 | } 451 | 452 | Ok(OsIpcSender::from_fd(fd)) 453 | } 454 | } 455 | } 456 | 457 | #[derive(PartialEq, Debug)] 458 | pub enum OsIpcChannel { 459 | Sender(OsIpcSender), 460 | Receiver(OsIpcReceiver), 461 | } 462 | 463 | impl OsIpcChannel { 464 | fn fd(&self) -> c_int { 465 | match *self { 466 | OsIpcChannel::Sender(ref sender) => sender.fd.0, 467 | OsIpcChannel::Receiver(ref receiver) => receiver.fd.get(), 468 | } 469 | } 470 | } 471 | 472 | pub struct OsIpcReceiverSet { 473 | incrementor: RangeFrom, 474 | poll: Poll, 475 | pollfds: HashMap>, 476 | events: Events, 477 | } 478 | 479 | impl Drop for OsIpcReceiverSet { 480 | fn drop(&mut self) { 481 | for &PollEntry { id: _, fd } in self.pollfds.values() { 482 | let result = unsafe { libc::close(fd) }; 483 | assert!(thread::panicking() || result == 0); 484 | } 485 | } 486 | } 487 | 488 | impl OsIpcReceiverSet { 489 | pub fn new() -> Result { 490 | let fnv = BuildHasherDefault::::default(); 491 | Ok(OsIpcReceiverSet { 492 | incrementor: 0.., 493 | poll: Poll::new()?, 494 | pollfds: HashMap::with_hasher(fnv), 495 | events: Events::with_capacity(10), 496 | }) 497 | } 498 | 499 | pub fn add(&mut self, receiver: OsIpcReceiver) -> Result { 500 | let last_index = self.incrementor.next().unwrap(); 501 | let fd = receiver.consume_fd(); 502 | let fd_token = Token(fd as usize); 503 | let poll_entry = PollEntry { id: last_index, fd }; 504 | self.poll 505 | .registry() 506 | .register(&mut SourceFd(&fd), fd_token, Interest::READABLE)?; 507 | self.pollfds.insert(fd_token, poll_entry); 508 | Ok(last_index) 509 | } 510 | 511 | pub fn select(&mut self) -> Result, UnixError> { 512 | // Poll until we receive at least one event. 513 | loop { 514 | match self.poll.poll(&mut self.events, None) { 515 | Ok(()) if !self.events.is_empty() => break, 516 | Ok(()) => {}, 517 | Err(ref error) => { 518 | if error.kind() != io::ErrorKind::Interrupted { 519 | return Err(UnixError::last()); 520 | } 521 | }, 522 | } 523 | 524 | if !self.events.is_empty() { 525 | break; 526 | } 527 | } 528 | 529 | let mut selection_results = Vec::new(); 530 | for event in self.events.iter() { 531 | // We only register this `Poll` for readable events. 532 | assert!(event.is_readable()); 533 | 534 | let event_token = event.token(); 535 | let poll_entry = *self 536 | .pollfds 537 | .get(&event_token) 538 | .expect("Got event for unknown token."); 539 | loop { 540 | match recv(poll_entry.fd, BlockingMode::Nonblocking) { 541 | Ok(ipc_message) => { 542 | selection_results.push(OsIpcSelectionResult::DataReceived( 543 | poll_entry.id, 544 | ipc_message, 545 | )); 546 | }, 547 | Err(err) if err.channel_is_closed() => { 548 | self.pollfds.remove(&event_token).unwrap(); 549 | self.poll 550 | .registry() 551 | .deregister(&mut SourceFd(&poll_entry.fd)) 552 | .unwrap(); 553 | unsafe { 554 | libc::close(poll_entry.fd); 555 | } 556 | 557 | selection_results.push(OsIpcSelectionResult::ChannelClosed(poll_entry.id)); 558 | break; 559 | }, 560 | Err(UnixError::Errno(code)) if code == EWOULDBLOCK => { 561 | // We tried to read another message from the file descriptor and 562 | // it would have blocked, so we have exhausted all of the data 563 | // pending to read. 564 | break; 565 | }, 566 | Err(err) => return Err(err), 567 | } 568 | } 569 | } 570 | 571 | Ok(selection_results) 572 | } 573 | } 574 | 575 | pub enum OsIpcSelectionResult { 576 | DataReceived(u64, IpcMessage), 577 | ChannelClosed(u64), 578 | } 579 | 580 | impl OsIpcSelectionResult { 581 | pub fn unwrap(self) -> (u64, IpcMessage) { 582 | match self { 583 | OsIpcSelectionResult::DataReceived(id, ipc_message) => (id, ipc_message), 584 | OsIpcSelectionResult::ChannelClosed(id) => { 585 | panic!("OsIpcSelectionResult::unwrap(): receiver ID {id} was closed!") 586 | }, 587 | } 588 | } 589 | } 590 | 591 | #[derive(PartialEq, Debug)] 592 | pub struct OsOpaqueIpcChannel { 593 | fd: c_int, 594 | } 595 | 596 | impl Drop for OsOpaqueIpcChannel { 597 | fn drop(&mut self) { 598 | // Make sure we don't leak! 599 | // 600 | // The `OsOpaqueIpcChannel` objects should always be used, 601 | // i.e. converted with `to_sender()` or `to_receiver()` -- 602 | // so the value should already be unset before the object gets dropped. 603 | debug_assert!(self.fd == -1); 604 | } 605 | } 606 | 607 | impl OsOpaqueIpcChannel { 608 | fn from_fd(fd: c_int) -> OsOpaqueIpcChannel { 609 | OsOpaqueIpcChannel { fd } 610 | } 611 | 612 | pub fn to_sender(&mut self) -> OsIpcSender { 613 | OsIpcSender::from_fd(mem::replace(&mut self.fd, -1)) 614 | } 615 | 616 | pub fn to_receiver(&mut self) -> OsIpcReceiver { 617 | OsIpcReceiver::from_fd(mem::replace(&mut self.fd, -1)) 618 | } 619 | } 620 | 621 | pub struct OsIpcOneShotServer { 622 | fd: c_int, 623 | 624 | // Object representing the temporary directory the socket was created in. 625 | // The directory is automatically deleted (along with the socket inside it) 626 | // when this field is dropped. 627 | _temp_dir: TempDir, 628 | } 629 | 630 | impl Drop for OsIpcOneShotServer { 631 | fn drop(&mut self) { 632 | unsafe { 633 | let result = libc::close(self.fd); 634 | assert!(thread::panicking() || result == 0); 635 | } 636 | } 637 | } 638 | 639 | impl OsIpcOneShotServer { 640 | pub fn new() -> Result<(OsIpcOneShotServer, String), UnixError> { 641 | unsafe { 642 | let fd = libc::socket(libc::AF_UNIX, SOCK_SEQPACKET | SOCK_FLAGS, 0); 643 | let temp_dir = Builder::new().tempdir()?; 644 | let socket_path = temp_dir.path().join("socket"); 645 | let path_string = socket_path.to_str().unwrap(); 646 | 647 | let path_c_string = CString::new(path_string).unwrap(); 648 | let (sockaddr, len) = new_sockaddr_un(path_c_string.as_ptr()); 649 | if libc::bind( 650 | fd, 651 | &sockaddr as *const _ as *const sockaddr, 652 | len as socklen_t, 653 | ) != 0 654 | { 655 | return Err(UnixError::last()); 656 | } 657 | 658 | if libc::listen(fd, 10) != 0 { 659 | return Err(UnixError::last()); 660 | } 661 | 662 | Ok(( 663 | OsIpcOneShotServer { 664 | fd, 665 | _temp_dir: temp_dir, 666 | }, 667 | path_string.to_string(), 668 | )) 669 | } 670 | } 671 | 672 | #[allow(clippy::type_complexity)] 673 | pub fn accept(self) -> Result<(OsIpcReceiver, IpcMessage), UnixError> { 674 | unsafe { 675 | let sockaddr: *mut sockaddr = ptr::null_mut(); 676 | let sockaddr_len: *mut socklen_t = ptr::null_mut(); 677 | let client_fd = libc::accept(self.fd, sockaddr, sockaddr_len); 678 | if client_fd < 0 { 679 | return Err(UnixError::last()); 680 | } 681 | make_socket_lingering(client_fd)?; 682 | 683 | let receiver = OsIpcReceiver::from_fd(client_fd); 684 | let ipc_message = receiver.recv()?; 685 | Ok((receiver, ipc_message)) 686 | } 687 | } 688 | } 689 | 690 | // Make sure that the kernel doesn't return errors to readers if there's still data left after we 691 | // close our end. 692 | // 693 | // See, for example, https://github.com/servo/ipc-channel/issues/29 694 | fn make_socket_lingering(sockfd: c_int) -> Result<(), UnixError> { 695 | let linger = linger { 696 | l_onoff: 1, 697 | l_linger: 30, 698 | }; 699 | let err = unsafe { 700 | setsockopt( 701 | sockfd, 702 | SOL_SOCKET, 703 | SO_LINGER, 704 | &linger as *const _ as *const c_void, 705 | mem::size_of::() as socklen_t, 706 | ) 707 | }; 708 | if err < 0 { 709 | let error = UnixError::last(); 710 | if let UnixError::Errno(libc::EINVAL) = error { 711 | // If the other side of the connection is already closed, POSIX.1-2024 (and earlier 712 | // versions) require that setsockopt return EINVAL [1]. This is a bit unfortunate 713 | // because SO_LINGER for a closed socket is logically a no-op, which is why some OSes 714 | // like Linux don't follow this part of the spec. But other OSes like illumos do return 715 | // EINVAL here. 716 | // 717 | // SO_LINGER is widely understood and EINVAL should not occur for any other reason, so 718 | // accept those errors. 719 | // 720 | // Another option would be to call make_socket_lingering on the initial socket created 721 | // by libc::socket, but whether accept inherits a particular option is 722 | // implementation-defined [2]. This means that special-casing EINVAL is the most 723 | // portable thing to do. 724 | // 725 | // [1] https://pubs.opengroup.org/onlinepubs/9799919799/functions/setsockopt.html: 726 | // "[EINVAL] The specified option is invalid at the specified socket level or the 727 | // socket has been shut down." 728 | // 729 | // [2] https://pubs.opengroup.org/onlinepubs/9799919799/functions/accept.html: "It is 730 | // implementation-defined which socket options, if any, on the accepted socket will 731 | // have a default value determined by a value previously customized by setsockopt() 732 | // on socket, rather than the default value used for other new sockets." 733 | } else { 734 | return Err(error); 735 | } 736 | } 737 | Ok(()) 738 | } 739 | 740 | struct BackingStore { 741 | fd: c_int, 742 | } 743 | 744 | impl BackingStore { 745 | pub fn new(length: usize) -> BackingStore { 746 | let count = SHM_COUNT.fetch_add(1, Ordering::Relaxed); 747 | let timestamp = UNIX_EPOCH.elapsed().unwrap(); 748 | let name = CString::new(format!( 749 | "/ipc-channel-shared-memory.{}.{}.{}.{}", 750 | count, 751 | *PID, 752 | timestamp.as_secs(), 753 | timestamp.subsec_nanos() 754 | )) 755 | .unwrap(); 756 | let fd = create_shmem(name, length); 757 | Self::from_fd(fd) 758 | } 759 | 760 | pub fn from_fd(fd: c_int) -> BackingStore { 761 | BackingStore { fd } 762 | } 763 | 764 | pub fn fd(&self) -> c_int { 765 | self.fd 766 | } 767 | 768 | pub unsafe fn map_file(&self, length: Option) -> (*mut u8, size_t) { 769 | let length = length.unwrap_or_else(|| { 770 | let mut st = mem::MaybeUninit::uninit(); 771 | if libc::fstat(self.fd, st.as_mut_ptr()) != 0 { 772 | panic!("error stating fd {}: {}", self.fd, UnixError::last()); 773 | } 774 | st.assume_init().st_size as size_t 775 | }); 776 | if length == 0 { 777 | // This will cause `mmap` to fail, so handle it explicitly. 778 | return (ptr::null_mut(), length); 779 | } 780 | let address = libc::mmap( 781 | ptr::null_mut(), 782 | length, 783 | PROT_READ | PROT_WRITE, 784 | MAP_SHARED, 785 | self.fd, 786 | 0, 787 | ); 788 | assert!(!address.is_null()); 789 | assert!(address != MAP_FAILED); 790 | (address as *mut u8, length) 791 | } 792 | } 793 | 794 | impl Drop for BackingStore { 795 | fn drop(&mut self) { 796 | unsafe { 797 | let result = libc::close(self.fd); 798 | assert!(thread::panicking() || result == 0); 799 | } 800 | } 801 | } 802 | 803 | pub struct OsIpcSharedMemory { 804 | ptr: *mut u8, 805 | length: usize, 806 | store: BackingStore, 807 | } 808 | 809 | unsafe impl Send for OsIpcSharedMemory {} 810 | unsafe impl Sync for OsIpcSharedMemory {} 811 | 812 | impl Drop for OsIpcSharedMemory { 813 | fn drop(&mut self) { 814 | unsafe { 815 | if !self.ptr.is_null() { 816 | let result = libc::munmap(self.ptr as *mut c_void, self.length); 817 | assert!(thread::panicking() || result == 0); 818 | } 819 | } 820 | } 821 | } 822 | 823 | impl Clone for OsIpcSharedMemory { 824 | fn clone(&self) -> OsIpcSharedMemory { 825 | unsafe { 826 | let store = BackingStore::from_fd(libc::dup(self.store.fd())); 827 | let (address, _) = store.map_file(Some(self.length)); 828 | OsIpcSharedMemory::from_raw_parts(address, self.length, store) 829 | } 830 | } 831 | } 832 | 833 | impl PartialEq for OsIpcSharedMemory { 834 | fn eq(&self, other: &OsIpcSharedMemory) -> bool { 835 | **self == **other 836 | } 837 | } 838 | 839 | impl Debug for OsIpcSharedMemory { 840 | fn fmt(&self, formatter: &mut Formatter) -> Result<(), fmt::Error> { 841 | (**self).fmt(formatter) 842 | } 843 | } 844 | 845 | impl Deref for OsIpcSharedMemory { 846 | type Target = [u8]; 847 | 848 | #[inline] 849 | fn deref(&self) -> &[u8] { 850 | unsafe { slice::from_raw_parts(self.ptr, self.length) } 851 | } 852 | } 853 | 854 | impl OsIpcSharedMemory { 855 | /// # Safety 856 | /// 857 | /// This is safe if there is only one reader/writer on the data. 858 | /// User can achieve this by not cloning [`IpcSharedMemory`] 859 | /// and serializing/deserializing only once. 860 | #[inline] 861 | pub unsafe fn deref_mut(&mut self) -> &mut [u8] { 862 | unsafe { slice::from_raw_parts_mut(self.ptr, self.length) } 863 | } 864 | } 865 | 866 | impl OsIpcSharedMemory { 867 | unsafe fn from_raw_parts( 868 | ptr: *mut u8, 869 | length: usize, 870 | store: BackingStore, 871 | ) -> OsIpcSharedMemory { 872 | OsIpcSharedMemory { ptr, length, store } 873 | } 874 | 875 | unsafe fn from_fd(fd: c_int) -> OsIpcSharedMemory { 876 | let store = BackingStore::from_fd(fd); 877 | let (ptr, length) = store.map_file(None); 878 | OsIpcSharedMemory::from_raw_parts(ptr, length, store) 879 | } 880 | 881 | pub fn from_byte(byte: u8, length: usize) -> OsIpcSharedMemory { 882 | unsafe { 883 | let store = BackingStore::new(length); 884 | let (address, _) = store.map_file(Some(length)); 885 | for element in slice::from_raw_parts_mut(address, length) { 886 | *element = byte; 887 | } 888 | OsIpcSharedMemory::from_raw_parts(address, length, store) 889 | } 890 | } 891 | 892 | pub fn from_bytes(bytes: &[u8]) -> OsIpcSharedMemory { 893 | unsafe { 894 | let store = BackingStore::new(bytes.len()); 895 | let (address, _) = store.map_file(Some(bytes.len())); 896 | ptr::copy_nonoverlapping(bytes.as_ptr(), address, bytes.len()); 897 | OsIpcSharedMemory::from_raw_parts(address, bytes.len(), store) 898 | } 899 | } 900 | } 901 | 902 | #[derive(Debug)] 903 | pub enum UnixError { 904 | Errno(c_int), 905 | ChannelClosed, 906 | IoError(io::Error), 907 | } 908 | 909 | impl UnixError { 910 | fn last() -> UnixError { 911 | UnixError::Errno(io::Error::last_os_error().raw_os_error().unwrap()) 912 | } 913 | 914 | #[allow(dead_code)] 915 | pub fn channel_is_closed(&self) -> bool { 916 | matches!(self, UnixError::ChannelClosed) 917 | } 918 | } 919 | 920 | impl fmt::Display for UnixError { 921 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 922 | match self { 923 | UnixError::Errno(errno) => { 924 | fmt::Display::fmt(&io::Error::from_raw_os_error(*errno), fmt) 925 | }, 926 | UnixError::ChannelClosed => write!(fmt, "All senders for this socket closed"), 927 | UnixError::IoError(e) => write!(fmt, "{e}"), 928 | } 929 | } 930 | } 931 | 932 | impl StdError for UnixError {} 933 | 934 | impl From for bincode::Error { 935 | fn from(unix_error: UnixError) -> Self { 936 | io::Error::from(unix_error).into() 937 | } 938 | } 939 | 940 | impl From for io::Error { 941 | fn from(unix_error: UnixError) -> io::Error { 942 | match unix_error { 943 | UnixError::Errno(errno) => io::Error::from_raw_os_error(errno), 944 | UnixError::ChannelClosed => io::Error::new(io::ErrorKind::ConnectionReset, unix_error), 945 | UnixError::IoError(e) => e, 946 | } 947 | } 948 | } 949 | 950 | impl From for ipc::IpcError { 951 | fn from(error: UnixError) -> Self { 952 | match error { 953 | UnixError::ChannelClosed => ipc::IpcError::Disconnected, 954 | e => ipc::IpcError::Io(io::Error::from(e)), 955 | } 956 | } 957 | } 958 | 959 | impl From for ipc::TryRecvError { 960 | fn from(error: UnixError) -> Self { 961 | match error { 962 | UnixError::ChannelClosed => ipc::TryRecvError::IpcError(ipc::IpcError::Disconnected), 963 | UnixError::Errno(code) if code == EAGAIN || code == EWOULDBLOCK => { 964 | ipc::TryRecvError::Empty 965 | }, 966 | e => ipc::TryRecvError::IpcError(ipc::IpcError::Io(io::Error::from(e))), 967 | } 968 | } 969 | } 970 | 971 | impl From for UnixError { 972 | fn from(e: io::Error) -> UnixError { 973 | if let Some(errno) = e.raw_os_error() { 974 | UnixError::Errno(errno) 975 | } else { 976 | assert!(e.kind() == io::ErrorKind::ConnectionReset); 977 | UnixError::ChannelClosed 978 | } 979 | } 980 | } 981 | 982 | #[derive(Copy, Clone)] 983 | enum BlockingMode { 984 | Blocking, 985 | Nonblocking, 986 | Timeout(Duration), 987 | } 988 | 989 | #[allow(clippy::uninit_vec, clippy::type_complexity)] 990 | fn recv(fd: c_int, blocking_mode: BlockingMode) -> Result { 991 | let (mut channels, mut shared_memory_regions) = (Vec::new(), Vec::new()); 992 | 993 | // First fragments begins with a header recording the total data length. 994 | // 995 | // We use this to determine whether we already got the entire message, 996 | // or need to receive additional fragments -- and if so, how much. 997 | let mut total_size = 0usize; 998 | let mut main_data_buffer; 999 | unsafe { 1000 | // Allocate a buffer without initialising the memory. 1001 | main_data_buffer = Vec::with_capacity(OsIpcSender::get_max_fragment_size()); 1002 | main_data_buffer.set_len(OsIpcSender::get_max_fragment_size()); 1003 | 1004 | let mut iovec = [ 1005 | iovec { 1006 | iov_base: &mut total_size as *mut _ as *mut c_void, 1007 | iov_len: mem::size_of_val(&total_size), 1008 | }, 1009 | iovec { 1010 | iov_base: main_data_buffer.as_mut_ptr() as *mut c_void, 1011 | iov_len: main_data_buffer.len(), 1012 | }, 1013 | ]; 1014 | let mut cmsg = UnixCmsg::new(&mut iovec)?; 1015 | 1016 | let bytes_read = cmsg.recv(fd, blocking_mode)?; 1017 | main_data_buffer.set_len(bytes_read - mem::size_of_val(&total_size)); 1018 | 1019 | let cmsg_fds = CMSG_DATA(cmsg.cmsg_buffer) as *const c_int; 1020 | let cmsg_length = cmsg.msghdr.msg_controllen; 1021 | let channel_length = if cmsg_length == 0 { 1022 | 0 1023 | } else { 1024 | // The control header is followed by an array of FDs. The size of the control header is 1025 | // determined by CMSG_SPACE. (On Linux this would the same as CMSG_ALIGN, but that isn't 1026 | // exposed by libc. CMSG_SPACE(0) is the portable version of that.) 1027 | (cmsg.cmsg_len() - CMSG_SPACE(0) as size_t) / mem::size_of::() 1028 | }; 1029 | for index in 0..channel_length { 1030 | let fd = *cmsg_fds.add(index); 1031 | if is_socket(fd) { 1032 | channels.push(OsOpaqueIpcChannel::from_fd(fd)); 1033 | continue; 1034 | } 1035 | shared_memory_regions.push(OsIpcSharedMemory::from_fd(fd)); 1036 | } 1037 | } 1038 | 1039 | if total_size == main_data_buffer.len() { 1040 | // Fast path: no fragments. 1041 | return Ok(IpcMessage::new( 1042 | main_data_buffer, 1043 | channels, 1044 | shared_memory_regions, 1045 | )); 1046 | } 1047 | 1048 | // Reassemble fragments. 1049 | // 1050 | // The initial fragment carries the receive end of a dedicated channel 1051 | // through which all the remaining fragments will be coming in. 1052 | let dedicated_rx = channels.pop().unwrap().to_receiver(); 1053 | 1054 | // Extend the buffer to hold the entire message, without initialising the memory. 1055 | let len = main_data_buffer.len(); 1056 | main_data_buffer.reserve_exact(total_size - len); 1057 | 1058 | // Receive followup fragments directly into the main buffer. 1059 | while main_data_buffer.len() < total_size { 1060 | let write_pos = main_data_buffer.len(); 1061 | let end_pos = cmp::min( 1062 | write_pos + OsIpcSender::fragment_size(*SYSTEM_SENDBUF_SIZE), 1063 | total_size, 1064 | ); 1065 | let result = unsafe { 1066 | assert!(end_pos <= main_data_buffer.capacity()); 1067 | main_data_buffer.set_len(end_pos); 1068 | 1069 | // Integer underflow could make the following code unsound... 1070 | assert!(end_pos >= write_pos); 1071 | 1072 | // Note: we always use blocking mode for followup fragments, 1073 | // to make sure that once we start receiving a multi-fragment message, 1074 | // we don't abort in the middle of it... 1075 | let result = libc::recv( 1076 | dedicated_rx.fd.get(), 1077 | main_data_buffer[write_pos..].as_mut_ptr() as *mut c_void, 1078 | end_pos - write_pos, 1079 | 0, 1080 | ); 1081 | main_data_buffer.set_len(write_pos + cmp::max(result, 0) as usize); 1082 | result 1083 | }; 1084 | 1085 | match result.cmp(&0) { 1086 | cmp::Ordering::Greater => continue, 1087 | cmp::Ordering::Equal => return Err(UnixError::ChannelClosed), 1088 | cmp::Ordering::Less => return Err(UnixError::last()), 1089 | } 1090 | } 1091 | 1092 | Ok(IpcMessage::new( 1093 | main_data_buffer, 1094 | channels, 1095 | shared_memory_regions, 1096 | )) 1097 | } 1098 | 1099 | // https://github.com/servo/ipc-channel/issues/192 1100 | fn new_msghdr(iovec: &mut [iovec], cmsg_buffer: *mut cmsghdr, cmsg_space: MsgControlLen) -> msghdr { 1101 | let mut msghdr: msghdr = unsafe { mem::zeroed() }; 1102 | msghdr.msg_name = ptr::null_mut(); 1103 | msghdr.msg_namelen = 0; 1104 | msghdr.msg_iov = iovec.as_mut_ptr(); 1105 | msghdr.msg_iovlen = iovec.len() as IovLen; 1106 | msghdr.msg_control = cmsg_buffer as *mut c_void; 1107 | msghdr.msg_controllen = cmsg_space; 1108 | msghdr.msg_flags = 0; 1109 | msghdr 1110 | } 1111 | 1112 | fn create_shmem(name: CString, length: usize) -> c_int { 1113 | unsafe { 1114 | let fd = libc::memfd_create(name.as_ptr(), libc::MFD_CLOEXEC); 1115 | assert!(fd >= 0); 1116 | assert_eq!(libc::ftruncate(fd, length as off_t), 0); 1117 | fd 1118 | } 1119 | } 1120 | 1121 | struct UnixCmsg { 1122 | cmsg_buffer: *mut cmsghdr, 1123 | msghdr: msghdr, 1124 | } 1125 | 1126 | unsafe impl Send for UnixCmsg {} 1127 | 1128 | impl Drop for UnixCmsg { 1129 | fn drop(&mut self) { 1130 | unsafe { 1131 | libc::free(self.cmsg_buffer as *mut c_void); 1132 | } 1133 | } 1134 | } 1135 | 1136 | impl UnixCmsg { 1137 | unsafe fn new(iovec: &mut [iovec]) -> Result { 1138 | let cmsg_length = CMSG_SPACE(MAX_FDS_IN_CMSG * (mem::size_of::() as c_uint)); 1139 | let cmsg_buffer = libc::malloc(cmsg_length as usize) as *mut cmsghdr; 1140 | if cmsg_buffer.is_null() { 1141 | return Err(UnixError::last()); 1142 | } 1143 | Ok(UnixCmsg { 1144 | cmsg_buffer, 1145 | msghdr: new_msghdr(iovec, cmsg_buffer, cmsg_length as MsgControlLen), 1146 | }) 1147 | } 1148 | 1149 | unsafe fn recv(&mut self, fd: c_int, blocking_mode: BlockingMode) -> Result { 1150 | match blocking_mode { 1151 | BlockingMode::Nonblocking => { 1152 | if libc::fcntl(fd, libc::F_SETFL, libc::O_NONBLOCK) < 0 { 1153 | return Err(UnixError::last()); 1154 | } 1155 | }, 1156 | BlockingMode::Timeout(duration) => { 1157 | let events = libc::POLLIN | libc::POLLPRI | libc::POLLRDHUP; 1158 | 1159 | let mut fd = [libc::pollfd { 1160 | fd, 1161 | events, 1162 | revents: 0, 1163 | }]; 1164 | let result = libc::poll( 1165 | fd.as_mut_ptr(), 1166 | fd.len() as _, 1167 | duration.as_millis().try_into().unwrap_or(-1), 1168 | ); 1169 | 1170 | match result.cmp(&0) { 1171 | cmp::Ordering::Equal => return Err(UnixError::Errno(EAGAIN)), 1172 | cmp::Ordering::Less => return Err(UnixError::last()), 1173 | cmp::Ordering::Greater => {}, 1174 | } 1175 | }, 1176 | BlockingMode::Blocking => {}, 1177 | } 1178 | 1179 | let result = recvmsg(fd, &mut self.msghdr, RECVMSG_FLAGS); 1180 | 1181 | let result = match result.cmp(&0) { 1182 | cmp::Ordering::Equal => Err(UnixError::ChannelClosed), 1183 | cmp::Ordering::Less => Err(UnixError::last()), 1184 | cmp::Ordering::Greater => Ok(result as usize), 1185 | }; 1186 | 1187 | if let BlockingMode::Nonblocking = blocking_mode { 1188 | if libc::fcntl(fd, libc::F_SETFL, 0) < 0 { 1189 | return Err(UnixError::last()); 1190 | } 1191 | } 1192 | result 1193 | } 1194 | 1195 | unsafe fn cmsg_len(&self) -> size_t { 1196 | (*(self.msghdr.msg_control as *const cmsghdr)).cmsg_len as size_t 1197 | } 1198 | } 1199 | 1200 | fn is_socket(fd: c_int) -> bool { 1201 | unsafe { 1202 | let mut st = mem::MaybeUninit::uninit(); 1203 | if libc::fstat(fd, st.as_mut_ptr()) != 0 { 1204 | return false; 1205 | } 1206 | (st.assume_init().st_mode & S_IFMT) == S_IFSOCK 1207 | } 1208 | } 1209 | -------------------------------------------------------------------------------- /src/platform/windows/aliased_cell.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The Servo Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution. 3 | // 4 | // Licensed under the Apache License, Version 2.0 or the MIT license 6 | // , at your 7 | // option. This file may not be copied, modified, or distributed 8 | // except according to those terms. 9 | 10 | use std::mem::{self, ManuallyDrop}; 11 | use std::thread; 12 | 13 | /// Dummy type that panics when its destructor is invoked. 14 | #[derive(Debug)] 15 | struct DropBomb(); 16 | 17 | impl Drop for DropBomb { 18 | fn drop(&mut self) { 19 | let message = "Trying to drop an AliasedCell, which may still have aliases outstanding."; 20 | if thread::panicking() { 21 | eprintln!("{}", message); 22 | } else { 23 | panic!("{}", message); 24 | } 25 | } 26 | } 27 | 28 | /// A wrapper for unsafely aliased memory locations. 29 | /// 30 | /// `AliasedCell' makes sure that its inner value 31 | /// is completely inaccessible from safe code. 32 | /// Once an `AliasedCell` has been constructed from some value, 33 | /// until the value is moved out again with the (unsafe) `into_inner()` method, 34 | /// the only way to access the wrapped value 35 | /// is through the unsafe `alias_mut()` method. 36 | /// 37 | /// This is useful for FFI calls that take raw pointers as input, 38 | /// and hold on to them even after returning control to the caller. 39 | /// Since Rust's type system is not aware of such aliases, 40 | /// it cannot provide the usual guarantees about validity of pointers 41 | /// and exclusiveness of mutable pointers. 42 | /// This means that any code that has access to the memory in question 43 | /// is inherently unsafe as long as such untracked aliases exist. 44 | /// Putting the value in an `AliasedCell` before the FFI call, 45 | /// and only taking it out again 46 | /// once the caller has ensured that all aliases have been dropped 47 | /// (most likely through another FFI call), 48 | /// makes certain that any such unsafe access to the aliased value 49 | /// can only happen from code marked as `unsafe`. 50 | /// 51 | /// An `AliasedCell` should never be simply dropped in normal use. 52 | /// Rather, it should always be freed explicitly with `into_inner()`, 53 | /// signalling that there are no outstanding aliases anymore. 54 | /// When an `AliasedCell` is dropped unexpectedly, 55 | /// we have to assume that it likely still has untracked aliases, 56 | /// and thus freeing the memory would probably be unsound. 57 | /// Therefore, the `drop()` implementation of `AliasedCell` 58 | /// leaks the inner value instead of freeing it; 59 | /// and throws a panic. 60 | /// Leaking the memory, while undesirable in general, 61 | /// keeps the memory accessible to any outstanding aliases. 62 | /// This is the only way to retain soundness during unwinding, 63 | /// or when the panic gets caught. 64 | /// 65 | /// Note that making FFI access through untracked aliases 66 | /// requires the value to have a stable memory location -- 67 | /// typically by living on the heap rather than on the stack. 68 | /// If the value isn't already in a heap-allocated container 69 | /// such as `Box<>`, `Vec<>`, or `String`, 70 | /// it is the caller's responsibility to wrap it in a `Box<>` explicitly. 71 | /// `AliasedCell` itself cannot ensure that the address remains stable 72 | /// when the `AliasedCell` gets moved around. 73 | #[derive(Debug)] 74 | pub struct AliasedCell { 75 | value: ManuallyDrop, 76 | drop_bomb: DropBomb, 77 | } 78 | 79 | impl AliasedCell { 80 | /// Wrap the provided value in an `AliasedCell`, making it inaccessible from safe code. 81 | pub fn new(value: T) -> AliasedCell { 82 | AliasedCell { 83 | value: ManuallyDrop::new(value), 84 | drop_bomb: DropBomb(), 85 | } 86 | } 87 | 88 | /// Get a pointer to the inner value. 89 | /// 90 | /// Note that this yields a regular reference. 91 | /// To actually get an untracked alias, 92 | /// it needs to be cast or coerced into a raw pointer. 93 | /// This usually happens implicitly though 94 | /// when calling an FFI function (or any other function) 95 | /// taking a raw pointer as argument. 96 | /// 97 | /// `alias_mut()` can be called any number of times: 98 | /// the wrapper doesn't keep track of the number of outstanding aliases -- 99 | /// the caller is responsible for making sure that no aliases are left 100 | /// before invoking `into_inner()`. 101 | /// If you need to track the number of aliases, 102 | /// wrap the inner value in an `Rc<>` or `Arc` -- 103 | /// this way, the reference count will also be inaccessible from safe code. 104 | pub unsafe fn alias_mut(&mut self) -> &mut T { 105 | &mut self.value 106 | } 107 | 108 | /// Get a shared (immutable) pointer to the inner value. 109 | /// 110 | /// With this method it's possible to get an alias 111 | /// while only holding a shared reference to the `AliasedCell`. 112 | /// 113 | /// Since all the unsafe aliases are untracked, 114 | /// it's up to the callers to make sure no shared aliases are used 115 | /// while the data might actually be mutated elsewhere 116 | /// through some outstanding mutable aliases. 117 | pub unsafe fn alias(&self) -> &T { 118 | &self.value // orig &self.inner 119 | } 120 | 121 | /// Move out the wrapped value, making it accessible from safe code again. 122 | pub unsafe fn into_inner(self) -> T { 123 | mem::forget(self.drop_bomb); 124 | ManuallyDrop::into_inner(self.value) 125 | } 126 | } 127 | 128 | /// Some basic tests. 129 | /// 130 | /// Note: These mostly just check that various expected usage scenarios 131 | /// can be compiled and basically work. 132 | /// We can't verify though that the usage is actually sound; 133 | /// nor do we check whether invalid usage is indeed prevented by the compiler... 134 | /// 135 | /// (The latter could probably be remedied though 136 | /// with some sort of compile-fail tests.) 137 | #[cfg(test)] 138 | mod tests { 139 | use super::AliasedCell; 140 | 141 | unsafe fn mutate_value(addr: *mut [i32; 4]) { 142 | let value = addr.as_mut().unwrap(); 143 | value[1] += value[3]; 144 | } 145 | 146 | struct Mutator { 147 | addr: *mut [i32; 4], 148 | ascended: bool, 149 | } 150 | 151 | impl Mutator { 152 | unsafe fn new(addr: *mut [i32; 4]) -> Mutator { 153 | Mutator { 154 | addr, 155 | ascended: false, 156 | } 157 | } 158 | 159 | fn ascend(&mut self) { 160 | self.ascended = true; 161 | } 162 | 163 | unsafe fn mutate(&mut self) { 164 | let value = self.addr.as_mut().unwrap(); 165 | if self.ascended { 166 | value[3] += value[2]; 167 | } else { 168 | value[1] += value[3]; 169 | } 170 | } 171 | } 172 | 173 | #[test] 174 | fn noop_roundtrip() { 175 | let value = [1, 3, 3, 7]; 176 | let cell = AliasedCell::new(Box::new(value)); 177 | let new_value = unsafe { *cell.into_inner() }; 178 | assert_eq!(new_value, [1, 3, 3, 7]); 179 | } 180 | 181 | #[test] 182 | fn unused_alias() { 183 | let value = [1, 3, 3, 7]; 184 | let mut cell = AliasedCell::new(Box::new(value)); 185 | unsafe { 186 | cell.alias_mut().as_mut(); 187 | } 188 | let new_value = unsafe { *cell.into_inner() }; 189 | assert_eq!(new_value, [1, 3, 3, 7]); 190 | } 191 | 192 | #[test] 193 | fn mutate() { 194 | let value = [1, 3, 3, 7]; 195 | let mut cell = AliasedCell::new(Box::new(value)); 196 | unsafe { 197 | mutate_value(cell.alias_mut().as_mut()); 198 | } 199 | let new_value = unsafe { *cell.into_inner() }; 200 | assert_eq!(new_value, [1, 10, 3, 7]); 201 | } 202 | 203 | /// Verify that we can take multiple aliases. 204 | #[test] 205 | fn mutate_twice() { 206 | let value = [1, 3, 3, 7]; 207 | let mut cell = AliasedCell::new(Box::new(value)); 208 | unsafe { 209 | mutate_value(cell.alias_mut().as_mut()); 210 | } 211 | unsafe { 212 | mutate_value(cell.alias_mut().as_mut()); 213 | } 214 | let new_value = unsafe { *cell.into_inner() }; 215 | assert_eq!(new_value, [1, 17, 3, 7]); 216 | } 217 | 218 | /// Verify that we can do basic safe manipulations between unsafe blocks. 219 | #[test] 220 | fn moves() { 221 | let value = [1, 3, 3, 7]; 222 | let mut cell = AliasedCell::new(Box::new(value)); 223 | unsafe { 224 | mutate_value(cell.alias_mut().as_mut()); 225 | } 226 | let mut cell2 = cell; 227 | unsafe { 228 | mutate_value(cell2.alias_mut().as_mut()); 229 | } 230 | let cell3 = cell2; 231 | let new_value = unsafe { *cell3.into_inner() }; 232 | assert_eq!(new_value, [1, 17, 3, 7]); 233 | } 234 | 235 | /// Verify that alias can be used at a later point. 236 | #[test] 237 | fn mutate_deferred() { 238 | let value = [1, 3, 3, 7]; 239 | let mut cell = AliasedCell::new(Box::new(value)); 240 | let mut mutator = unsafe { Mutator::new(cell.alias_mut().as_mut()) }; 241 | unsafe { 242 | mutator.mutate(); 243 | } 244 | let new_value = unsafe { *cell.into_inner() }; 245 | assert_eq!(new_value, [1, 10, 3, 7]); 246 | } 247 | 248 | #[test] 249 | fn mutate_deferred_twice() { 250 | let value = [1, 3, 3, 7]; 251 | let mut cell = AliasedCell::new(Box::new(value)); 252 | let mut mutator = unsafe { Mutator::new(cell.alias_mut().as_mut()) }; 253 | unsafe { 254 | mutator.mutate(); 255 | } 256 | unsafe { 257 | mutator.mutate(); 258 | } 259 | let new_value = unsafe { *cell.into_inner() }; 260 | assert_eq!(new_value, [1, 17, 3, 7]); 261 | } 262 | 263 | /// Further safe manipulations. 264 | #[test] 265 | fn deferred_moves() { 266 | let value = [1, 3, 3, 7]; 267 | let mut cell = AliasedCell::new(Box::new(value)); 268 | let mutator = unsafe { Mutator::new(cell.alias_mut().as_mut()) }; 269 | let cell2 = cell; 270 | let mut mutator2 = mutator; 271 | unsafe { 272 | mutator2.mutate(); 273 | } 274 | let cell3 = cell2; 275 | let _mutator3 = mutator2; 276 | let new_value = unsafe { *cell3.into_inner() }; 277 | assert_eq!(new_value, [1, 10, 3, 7]); 278 | } 279 | 280 | /// Non-trivial safe manipulation. 281 | #[test] 282 | fn safe_frobbing() { 283 | let value = [1, 3, 3, 7]; 284 | let mut cell = AliasedCell::new(Box::new(value)); 285 | let mut mutator = unsafe { Mutator::new(cell.alias_mut().as_mut()) }; 286 | unsafe { 287 | mutator.mutate(); 288 | } 289 | mutator.ascend(); 290 | unsafe { 291 | mutator.mutate(); 292 | } 293 | let new_value = unsafe { *cell.into_inner() }; 294 | assert_eq!(new_value, [1, 10, 3, 10]); 295 | } 296 | 297 | /// Verify that two aliases can exist simultaneously. 298 | #[test] 299 | fn two_mutators() { 300 | let value = [1, 3, 3, 7]; 301 | let mut cell = AliasedCell::new(Box::new(value)); 302 | let mut mutator1 = unsafe { Mutator::new(cell.alias_mut().as_mut()) }; 303 | unsafe { 304 | mutator1.mutate(); 305 | } 306 | let mut mutator2 = unsafe { Mutator::new(cell.alias_mut().as_mut()) }; 307 | unsafe { 308 | mutator2.mutate(); 309 | } 310 | let new_value = unsafe { *cell.into_inner() }; 311 | assert_eq!(new_value, [1, 17, 3, 7]); 312 | } 313 | 314 | #[test] 315 | #[should_panic( 316 | expected = "Trying to drop an AliasedCell, which may still have aliases outstanding." 317 | )] 318 | fn invalid_drop() { 319 | let value = [1, 3, 3, 7]; 320 | let mut cell = AliasedCell::new(Box::new(value)); 321 | unsafe { 322 | let mut mutator = Mutator::new(cell.alias_mut().as_mut()); 323 | mutator.mutate(); 324 | drop(cell); 325 | } 326 | } 327 | 328 | /// Verify that we skip the panic-on-drop while unwinding from another panic. 329 | #[test] 330 | #[should_panic(expected = "bye!")] 331 | fn panic() { 332 | let value = [1, 3, 3, 7]; 333 | let mut cell = AliasedCell::new(Box::new(value)); 334 | unsafe { 335 | let mut mutator = Mutator::new(cell.alias_mut().as_mut()); 336 | mutator.mutate(); 337 | panic!("bye!"); 338 | } 339 | } 340 | } 341 | -------------------------------------------------------------------------------- /src/platform/windows/tests.rs: -------------------------------------------------------------------------------- 1 | use crate::platform::windows::{channel, OutOfBandMessage}; 2 | use crate::platform::OsIpcSharedMemory; 3 | use windows::core::{HRESULT, PCSTR}; 4 | use windows::Win32::Foundation::{ 5 | CloseHandle, CompareObjectHandles, ERROR_INVALID_HANDLE, HANDLE, INVALID_HANDLE_VALUE, 6 | }; 7 | use windows::Win32::System::Memory::{CreateFileMappingA, PAGE_READWRITE}; 8 | use windows::Win32::System::Threading::{CreateEventA, GetCurrentProcessId}; 9 | 10 | #[test] 11 | fn test_recover_handles_empty() { 12 | let target_process_id = std::process::id(); 13 | let mut oob = OutOfBandMessage::new(target_process_id); 14 | assert!(oob.recover_handles().is_ok()); 15 | } 16 | 17 | #[test] 18 | fn test_recover_handles_duplicates_channel_handles() { 19 | let mut handles = Vec::new(); 20 | 21 | // Create some dummy event handles 22 | for _ in 0..3 { 23 | let event = 24 | unsafe { CreateEventA(None, false, false, None) }.expect("Failed to create event"); 25 | handles.push(event.0 as isize); 26 | } 27 | 28 | let mut oob = OutOfBandMessage { 29 | target_process_id: std::process::id(), 30 | channel_handles: handles.clone(), 31 | shmem_handles: vec![], 32 | big_data_receiver_handle: None, 33 | }; 34 | 35 | let result = oob.recover_handles(); 36 | 37 | assert!(result.is_ok()); 38 | assert_eq!(oob.channel_handles.len(), 3); 39 | for (i, handle) in oob.channel_handles.iter().enumerate() { 40 | assert_ne!(*handle, handles[i]); 41 | assert_ne!(*handle as isize, INVALID_HANDLE_VALUE.0 as isize); 42 | } 43 | 44 | // Clean up the handles 45 | for handle in handles { 46 | unsafe { CloseHandle(HANDLE(handle as _)) }.expect("Failed to close handle"); 47 | } 48 | for handle in oob.channel_handles { 49 | unsafe { CloseHandle(HANDLE(handle as _)) }.expect("Failed to close handle"); 50 | } 51 | } 52 | 53 | #[test] 54 | fn test_recover_handles_empty_channel_handles() { 55 | let mut oob = OutOfBandMessage { 56 | target_process_id: std::process::id(), 57 | channel_handles: vec![], 58 | shmem_handles: vec![], 59 | big_data_receiver_handle: None, 60 | }; 61 | 62 | let result = oob.recover_handles(); 63 | 64 | assert!(result.is_ok()); 65 | assert_eq!(oob.channel_handles.len(), 0); 66 | } 67 | 68 | #[test] 69 | fn test_recover_handles_duplicates_shmem_handles() { 70 | let mut handles = Vec::new(); 71 | let mut sizes = Vec::new(); 72 | 73 | // Create some dummy shared memory handles 74 | for _ in 0..3 { 75 | let file_mapping = unsafe { 76 | CreateFileMappingA( 77 | INVALID_HANDLE_VALUE, 78 | None, 79 | PAGE_READWRITE, 80 | 0, 81 | 1024, 82 | PCSTR::null(), 83 | ) 84 | } 85 | .expect("Failed to create file mapping"); 86 | handles.push(file_mapping.0 as isize); 87 | sizes.push(1024u64); 88 | } 89 | 90 | let mut oob = OutOfBandMessage { 91 | target_process_id: std::process::id(), 92 | channel_handles: vec![], 93 | shmem_handles: handles.clone().into_iter().zip(sizes.into_iter()).collect(), 94 | big_data_receiver_handle: None, 95 | }; 96 | 97 | let result = oob.recover_handles(); 98 | 99 | assert!(result.is_ok()); 100 | assert_eq!(oob.shmem_handles.len(), 3); 101 | for (handle, size) in &oob.shmem_handles { 102 | assert_ne!(*handle, INVALID_HANDLE_VALUE.0 as isize); 103 | assert_eq!(*size, 1024); 104 | 105 | // Verify that the new handle is valid and different from the original 106 | let new_handle = HANDLE(*handle as _); 107 | assert!(unsafe { CompareObjectHandles(new_handle, INVALID_HANDLE_VALUE) } == false); 108 | 109 | // Clean up the duplicated handle 110 | unsafe { CloseHandle(new_handle) }.expect("Failed to close duplicated handle"); 111 | } 112 | 113 | // Clean up the original handles 114 | for handle in handles.into_iter() { 115 | unsafe { CloseHandle(HANDLE(handle as _)) }.expect("Failed to close original handle"); 116 | } 117 | } 118 | 119 | #[test] 120 | fn test_recover_handles_empty_shmem_handles() { 121 | let mut oob = OutOfBandMessage { 122 | target_process_id: std::process::id(), 123 | channel_handles: vec![], 124 | shmem_handles: vec![], 125 | big_data_receiver_handle: None, 126 | }; 127 | 128 | let result = oob.recover_handles(); 129 | 130 | assert!(result.is_ok()); 131 | assert!(oob.shmem_handles.is_empty()); 132 | } 133 | 134 | #[test] 135 | fn test_recover_handles_duplicates_big_data_receiver_handle() { 136 | let event = unsafe { CreateEventA(None, false, false, None) }.expect("Failed to create event"); 137 | let event_handle = event.0 as isize; 138 | 139 | let mut oob = OutOfBandMessage { 140 | target_process_id: std::process::id(), 141 | channel_handles: vec![], 142 | shmem_handles: vec![], 143 | big_data_receiver_handle: Some((event_handle, 1024)), 144 | }; 145 | 146 | let result = oob.recover_handles(); 147 | 148 | assert!(result.is_ok()); 149 | if let Some((handle, _)) = oob.big_data_receiver_handle { 150 | assert_ne!(handle, event_handle); 151 | unsafe { 152 | let new_handle = HANDLE(handle as _); 153 | assert!(CompareObjectHandles(event, new_handle).as_bool()); 154 | CloseHandle(new_handle).expect("Failed to close duplicated handle"); 155 | } 156 | } else { 157 | panic!("big_data_receiver_handle is None after recovery"); 158 | } 159 | 160 | unsafe { CloseHandle(event).expect("Failed to close original event handle") }; 161 | } 162 | 163 | #[test] 164 | fn test_recover_handles_with_no_big_data_receiver() { 165 | let mut handles = Vec::new(); 166 | 167 | // Create some dummy event handles 168 | for _ in 0..5 { 169 | let event = 170 | unsafe { CreateEventA(None, false, false, None) }.expect("Failed to create event"); 171 | handles.push(event.0 as isize); 172 | } 173 | let mut oob = OutOfBandMessage { 174 | target_process_id: std::process::id(), 175 | channel_handles: vec![handles[0], handles[1], handles[2]], 176 | shmem_handles: vec![(handles[3], 100), (handles[4], 200)], 177 | big_data_receiver_handle: None, 178 | }; 179 | 180 | let result = oob.recover_handles(); 181 | 182 | assert!(result.is_ok()); 183 | assert_eq!(oob.channel_handles.len(), 3); 184 | assert_eq!(oob.shmem_handles.len(), 2); 185 | assert!(oob.big_data_receiver_handle.is_none()); 186 | 187 | // Verify that the handles have been duplicated (i.e., they're different from the original handles) 188 | for (i, handle) in oob.channel_handles.iter().enumerate() { 189 | assert_ne!(*handle, handles[i]); 190 | } 191 | for (i, (handle, _)) in oob.shmem_handles.iter().enumerate() { 192 | assert_ne!(*handle, handles[i + 3]); 193 | } 194 | 195 | // Clean up the original handles 196 | for handle in handles { 197 | unsafe { CloseHandle(HANDLE(handle as _)) }.expect("Failed to close handle"); 198 | } 199 | } 200 | 201 | #[test] 202 | fn test_recover_handles_fails_with_arbitrary_process_id() { 203 | let mut oob = OutOfBandMessage { 204 | target_process_id: 0, // Use 0 as an invalid process ID 205 | channel_handles: vec![], 206 | shmem_handles: vec![], 207 | big_data_receiver_handle: None, 208 | }; 209 | 210 | let result = oob.recover_handles(); 211 | 212 | assert!(result.is_err()); 213 | // The exact error code might vary, but it should be an error 214 | assert!(result.unwrap_err().code() != HRESULT(0i32)); 215 | } 216 | 217 | #[test] 218 | fn test_recover_handles_fails_with_invalid_handles() { 219 | let mut handles = Vec::new(); 220 | 221 | // Create some dummy event handles 222 | for _ in 0..3 { 223 | let event = 224 | unsafe { CreateEventA(None, false, false, None) }.expect("Failed to create event"); 225 | handles.push(event.0 as isize); 226 | } 227 | 228 | let mut oob = OutOfBandMessage { 229 | target_process_id: std::process::id(), 230 | channel_handles: handles.clone(), 231 | shmem_handles: vec![], 232 | big_data_receiver_handle: None, 233 | }; 234 | 235 | // Close the handles to make them invalid 236 | for handle in handles.clone().into_iter() { 237 | unsafe { CloseHandle(HANDLE(handle as _)).expect("Failed to close handle") }; 238 | } 239 | 240 | // Now try to recover the handles 241 | let result = oob.recover_handles(); 242 | 243 | // The recovery should fail because the handles are now invalid 244 | assert!(result.is_err()); 245 | 246 | // Check that the error is of the expected type 247 | match result.unwrap_err() { 248 | err if err.code() == ERROR_INVALID_HANDLE.to_hresult() => {}, 249 | err => panic!("Unexpected error: {:?}", err), 250 | } 251 | 252 | // Verify that the channel handles in oob are still the same (invalid) handles 253 | assert_eq!(oob.channel_handles, handles); 254 | } 255 | 256 | #[test] 257 | fn test_recover_handles_large_number_of_handles() { 258 | let mut oob = OutOfBandMessage { 259 | target_process_id: std::process::id(), 260 | channel_handles: Vec::new(), 261 | shmem_handles: Vec::new(), 262 | big_data_receiver_handle: None, 263 | }; 264 | 265 | const NUM_HANDLES: usize = 1000; 266 | 267 | // Create a large number of dummy event handles 268 | for _ in 0..NUM_HANDLES { 269 | let event = 270 | unsafe { CreateEventA(None, false, false, None) }.expect("Failed to create event"); 271 | oob.channel_handles.push(event.0 as isize); 272 | } 273 | 274 | // Add some dummy shared memory handles 275 | for _ in 0..NUM_HANDLES { 276 | let mapping = unsafe { 277 | CreateFileMappingA(INVALID_HANDLE_VALUE, None, PAGE_READWRITE, 0, 4096, None) 278 | } 279 | .expect("Failed to create file mapping"); 280 | oob.shmem_handles.push((mapping.0 as isize, 4096)); 281 | } 282 | 283 | // Add a dummy big data receiver handle 284 | let big_data_event = 285 | unsafe { CreateEventA(None, false, false, None) }.expect("Failed to create big data event"); 286 | oob.big_data_receiver_handle = Some((big_data_event.0 as isize, 8192)); 287 | 288 | // Call recover_handles 289 | oob.recover_handles().expect("Failed to recover handles"); 290 | 291 | // Verify that all handles were duplicated correctly 292 | for handle in &oob.channel_handles { 293 | assert_ne!(*handle, INVALID_HANDLE_VALUE.0 as isize); 294 | assert!(unsafe { CloseHandle(HANDLE(*handle as _)) }.is_ok()); 295 | } 296 | 297 | for (handle, _) in &oob.shmem_handles { 298 | assert_ne!(*handle, INVALID_HANDLE_VALUE.0 as isize); 299 | assert!(unsafe { CloseHandle(HANDLE(*handle as _)) }.is_ok()); 300 | } 301 | 302 | if let Some((handle, _)) = oob.big_data_receiver_handle { 303 | assert_ne!(handle, INVALID_HANDLE_VALUE.0 as isize); 304 | assert!(unsafe { CloseHandle(HANDLE(handle as _)) }.is_ok()); 305 | } 306 | 307 | assert_eq!(oob.channel_handles.len(), NUM_HANDLES); 308 | assert_eq!(oob.shmem_handles.len(), NUM_HANDLES); 309 | assert!(oob.big_data_receiver_handle.is_some()); 310 | } 311 | 312 | #[test] 313 | fn test_recover_handles_channel() { 314 | let (sender, _) = channel().unwrap(); 315 | let target_process_id = std::process::id(); 316 | let mut oob = OutOfBandMessage::new(target_process_id); 317 | 318 | oob.channel_handles.push(sender.handle.as_raw().0 as _); 319 | 320 | assert!(oob.recover_handles().is_ok()); 321 | assert_eq!(oob.channel_handles.len(), 1); 322 | assert_ne!(oob.channel_handles[0], sender.handle.as_raw().0 as _); 323 | 324 | // Clean up 325 | unsafe { CloseHandle(HANDLE(oob.channel_handles[0] as _)).unwrap() }; 326 | } 327 | 328 | #[test] 329 | fn test_recover_handles_shmem() { 330 | let shmem = OsIpcSharedMemory::new(1024).unwrap(); 331 | let target_process_id = std::process::id(); 332 | let mut oob = OutOfBandMessage::new(target_process_id); 333 | 334 | oob.shmem_handles.push((shmem.handle.as_raw().0 as _, 1024)); 335 | 336 | assert!(oob.recover_handles().is_ok()); 337 | assert_eq!(oob.shmem_handles.len(), 1); 338 | assert_ne!(oob.shmem_handles[0].0, shmem.handle.as_raw().0 as _); 339 | 340 | // Clean up 341 | unsafe { CloseHandle(HANDLE(oob.shmem_handles[0].0 as _)).unwrap() }; 342 | } 343 | 344 | #[test] 345 | fn test_recover_handles_different_process() { 346 | let current_process_id = std::process::id(); 347 | let target_process_id = current_process_id + 1; // Different from current process 348 | let mut oob = OutOfBandMessage::new(target_process_id); 349 | 350 | // Create some real handles 351 | let event_handle = unsafe { CreateEventA(None, true, false, None).unwrap() }; 352 | let file_mapping_handle = unsafe { 353 | CreateFileMappingA(INVALID_HANDLE_VALUE, None, PAGE_READWRITE, 0, 1024, None).unwrap() 354 | }; 355 | let big_data_event_handle = unsafe { CreateEventA(None, true, false, None).unwrap() }; 356 | 357 | // Add these handles to the OutOfBandMessage 358 | oob.channel_handles.push(event_handle.0 as isize); 359 | oob.shmem_handles 360 | .push((file_mapping_handle.0 as isize, 1024)); 361 | oob.big_data_receiver_handle = Some((big_data_event_handle.0 as isize, 2048)); 362 | 363 | // Execute the function 364 | assert!(oob.recover_handles().is_ok()); 365 | 366 | // Check that handles were duplicated correctly 367 | assert_ne!(oob.channel_handles[0], event_handle.0 as isize); 368 | assert_ne!(oob.shmem_handles[0].0, file_mapping_handle.0 as isize); 369 | assert_ne!( 370 | oob.big_data_receiver_handle.unwrap().0, 371 | big_data_event_handle.0 as isize 372 | ); 373 | 374 | // Verify that the new handles are valid 375 | unsafe { 376 | assert!(CompareObjectHandles(HANDLE(oob.channel_handles[0] as _), event_handle).as_bool()); 377 | assert!( 378 | CompareObjectHandles(HANDLE(oob.shmem_handles[0].0 as _), file_mapping_handle) 379 | .as_bool() 380 | ); 381 | assert!(CompareObjectHandles( 382 | HANDLE(oob.big_data_receiver_handle.unwrap().0 as _), 383 | big_data_event_handle 384 | ) 385 | .as_bool()); 386 | } 387 | 388 | // Clean up 389 | unsafe { 390 | CloseHandle(event_handle).unwrap(); 391 | CloseHandle(file_mapping_handle).unwrap(); 392 | CloseHandle(big_data_event_handle).unwrap(); 393 | CloseHandle(HANDLE(oob.channel_handles[0] as _)).unwrap(); 394 | CloseHandle(HANDLE(oob.shmem_handles[0].0 as _)).unwrap(); 395 | CloseHandle(HANDLE(oob.big_data_receiver_handle.unwrap().0 as _)).unwrap(); 396 | } 397 | } 398 | 399 | #[test] 400 | fn test_recover_handles_mixed_shmem_handles() { 401 | let target_process_id = std::process::id(); 402 | let mut oob = OutOfBandMessage::new(target_process_id); 403 | 404 | // Create a valid shared memory handle 405 | let valid_handle = unsafe { 406 | CreateFileMappingA( 407 | INVALID_HANDLE_VALUE, 408 | None, 409 | PAGE_READWRITE, 410 | 0, 411 | 1024, 412 | PCSTR::null(), 413 | ) 414 | } 415 | .expect("Failed to create file mapping"); 416 | 417 | // Add a mix of valid and invalid handles to shmem_handles 418 | oob.shmem_handles.push((valid_handle.0 as isize, 1024)); 419 | oob.shmem_handles 420 | .push((INVALID_HANDLE_VALUE.0 as isize, 512)); 421 | 422 | // Recover handles 423 | assert!(oob.recover_handles().is_ok()); 424 | 425 | // Verify that the valid handle was duplicated and the invalid handle remains unchanged 426 | assert_ne!(oob.shmem_handles[0].0, valid_handle.0 as isize); 427 | assert_ne!(oob.shmem_handles[0].0, INVALID_HANDLE_VALUE.0 as isize); 428 | assert_ne!(oob.shmem_handles[1].0, INVALID_HANDLE_VALUE.0 as isize); 429 | assert_ne!(oob.shmem_handles[1].0, target_process_id as isize); 430 | 431 | // Clean up 432 | unsafe { 433 | CloseHandle(valid_handle).expect("Failed to close valid handle"); 434 | CloseHandle(HANDLE(oob.shmem_handles[0].0 as _)) 435 | .expect("Failed to close duplicated handle"); 436 | CloseHandle(HANDLE(oob.shmem_handles[1].0 as _)).expect("Failed to close invalid handle"); 437 | } 438 | } 439 | 440 | #[test] 441 | fn test_recover_handles_mixed_validity() { 442 | let target_process_id = std::process::id(); 443 | let mut oob = OutOfBandMessage::new(target_process_id); 444 | 445 | // Create some valid handles 446 | let valid_handles: Vec = (0..3) 447 | .map(|_| unsafe { CreateEventA(None, false, false, None).unwrap() }) 448 | .collect(); 449 | 450 | // Mix valid and invalid handles 451 | oob.channel_handles = vec![ 452 | valid_handles[0].0 as isize, 453 | INVALID_HANDLE_VALUE.0 as isize, 454 | valid_handles[1].0 as isize, 455 | INVALID_HANDLE_VALUE.0 as isize, 456 | valid_handles[2].0 as isize, 457 | ]; 458 | 459 | assert!(oob.recover_handles().is_ok()); 460 | 461 | // Check that valid handles were duplicated and invalid ones were ignored 462 | assert!(oob.channel_handles.len() == 5); 463 | assert!(oob.channel_handles[0] != valid_handles[0].0 as isize); 464 | assert!(oob.channel_handles[1] != INVALID_HANDLE_VALUE.0 as isize); 465 | assert!(oob.channel_handles[2] != valid_handles[1].0 as isize); 466 | assert!(oob.channel_handles[3] != INVALID_HANDLE_VALUE.0 as isize); 467 | assert!(oob.channel_handles[4] != valid_handles[2].0 as isize); 468 | 469 | // Clean up 470 | for handle in valid_handles { 471 | unsafe { CloseHandle(handle).unwrap() }; 472 | } 473 | } 474 | 475 | #[test] 476 | fn test_recover_handles_invalid_process() { 477 | let mut oob = OutOfBandMessage::new(0); // Invalid process ID 478 | oob.channel_handles.push(1); // Dummy handle 479 | 480 | assert!(oob.recover_handles().is_err()); 481 | } 482 | -------------------------------------------------------------------------------- /src/router.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Servo Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution. 3 | // 4 | // Licensed under the Apache License, Version 2.0 or the MIT license 6 | // , at your 7 | // option. This file may not be copied, modified, or distributed 8 | // except according to those terms. 9 | 10 | //! Routers allow converting IPC channels to crossbeam channels. 11 | //! The [RouterProxy] provides various methods to register 12 | //! `IpcReceiver`s. The router will then either call the appropriate callback or route the 13 | //! message to a crossbeam `Sender` or `Receiver`. You should use the global `ROUTER` to 14 | //! access the `RouterProxy` methods (via `ROUTER`'s `Deref` for `RouterProxy`. 15 | 16 | use std::collections::HashMap; 17 | use std::sync::{LazyLock, Mutex}; 18 | use std::thread::{self, JoinHandle}; 19 | 20 | use crossbeam_channel::{self, Receiver, Sender}; 21 | use serde_core::{Deserialize, Serialize}; 22 | 23 | use crate::ipc::{ 24 | self, IpcMessage, IpcReceiver, IpcReceiverSet, IpcSelectionResult, IpcSender, OpaqueIpcReceiver, 25 | }; 26 | 27 | /// Global object wrapping a `RouterProxy`. 28 | /// Add routes ([add_route](RouterProxy::add_route)), or convert IpcReceiver 29 | /// to crossbeam channels (e.g. [route_ipc_receiver_to_new_crossbeam_receiver](RouterProxy::route_ipc_receiver_to_new_crossbeam_receiver)) 30 | pub static ROUTER: LazyLock = LazyLock::new(RouterProxy::new); 31 | 32 | /// A `RouterProxy` provides methods for talking to the router. Calling 33 | /// [new](RouterProxy::new) automatically spins up a router thread which 34 | /// waits for events on its registered `IpcReceiver`s. The `RouterProxy`'s 35 | /// methods communicate with the running router thread to register new 36 | /// `IpcReceiver`'s 37 | pub struct RouterProxy { 38 | comm: Mutex, 39 | } 40 | 41 | impl Drop for RouterProxy { 42 | fn drop(&mut self) { 43 | self.shutdown(); 44 | } 45 | } 46 | 47 | #[allow(clippy::new_without_default)] 48 | impl RouterProxy { 49 | pub fn new() -> RouterProxy { 50 | // Router acts like a receiver, running in its own thread with both 51 | // receiver ends. 52 | // Router proxy takes both sending ends. 53 | let (msg_sender, msg_receiver) = crossbeam_channel::unbounded(); 54 | let (wakeup_sender, wakeup_receiver) = ipc::channel().unwrap(); 55 | let handle = thread::Builder::new() 56 | .name("router-proxy".to_string()) 57 | .spawn(move || Router::new(msg_receiver, wakeup_receiver).run()) 58 | .expect("Failed to spawn router proxy thread"); 59 | RouterProxy { 60 | comm: Mutex::new(RouterProxyComm { 61 | msg_sender, 62 | wakeup_sender, 63 | shutdown: false, 64 | handle: Some(handle), 65 | }), 66 | } 67 | } 68 | 69 | /// Add a new (receiver, callback) pair to the router, and send a wakeup message 70 | /// to the router. 71 | fn add_route(&self, receiver: OpaqueIpcReceiver, callback: RouterHandler) { 72 | let comm = self.comm.lock().unwrap(); 73 | 74 | if comm.shutdown { 75 | return; 76 | } 77 | 78 | comm.msg_sender 79 | .send(RouterMsg::AddRoute(receiver, callback)) 80 | .unwrap(); 81 | comm.wakeup_sender.send(()).unwrap(); 82 | } 83 | 84 | /// Add a new `(receiver, callback)` pair to the router, and send a wakeup message 85 | /// to the router. 86 | pub fn add_typed_route( 87 | &self, 88 | receiver: IpcReceiver, 89 | mut callback: TypedRouterMultiHandler, 90 | ) where 91 | T: Serialize + for<'de> Deserialize<'de> + 'static, 92 | { 93 | // Before passing the message on to the callback, turn it into the appropriate type 94 | let modified_callback = move |msg: IpcMessage| { 95 | let typed_message = msg.to::(); 96 | callback(typed_message) 97 | }; 98 | 99 | self.add_route( 100 | receiver.to_opaque(), 101 | RouterHandler::Multi(Box::new(modified_callback)), 102 | ); 103 | } 104 | 105 | /// Add a new `(receiver, callback)` pair to the router, and send a wakeup message 106 | /// to the router. 107 | pub fn add_typed_one_shot_route( 108 | &self, 109 | receiver: IpcReceiver, 110 | callback: TypedRouterOneShotHandler, 111 | ) where 112 | T: Serialize + for<'de> Deserialize<'de> + 'static, 113 | { 114 | // Before passing the message on to the callback, turn it into the appropriate type 115 | let modified_callback = move |msg: IpcMessage| { 116 | let typed_message = msg.to::(); 117 | callback(typed_message) 118 | }; 119 | 120 | self.add_route( 121 | receiver.to_opaque(), 122 | RouterHandler::Once(Some(Box::new(modified_callback))), 123 | ); 124 | } 125 | 126 | /// Send a shutdown message to the router containing a ACK sender, 127 | /// send a wakeup message to the router, and block on the ACK. 128 | /// Calling it is idempotent, 129 | /// which can be useful when running a multi-process system in single-process mode. 130 | pub fn shutdown(&self) { 131 | let mut comm = self.comm.lock().unwrap(); 132 | 133 | if comm.shutdown { 134 | return; 135 | } 136 | comm.shutdown = true; 137 | 138 | let (ack_sender, ack_receiver) = crossbeam_channel::unbounded(); 139 | comm.wakeup_sender 140 | .send(()) 141 | .map(|_| { 142 | comm.msg_sender 143 | .send(RouterMsg::Shutdown(ack_sender)) 144 | .unwrap(); 145 | ack_receiver.recv().unwrap(); 146 | }) 147 | .unwrap(); 148 | comm.handle 149 | .take() 150 | .expect("Should have a join handle at shutdown") 151 | .join() 152 | .expect("Failed to join on the router proxy thread"); 153 | } 154 | 155 | /// A convenience function to route an `IpcReceiver` to an existing `Sender`. 156 | pub fn route_ipc_receiver_to_crossbeam_sender( 157 | &self, 158 | ipc_receiver: IpcReceiver, 159 | crossbeam_sender: Sender, 160 | ) where 161 | T: for<'de> Deserialize<'de> + Serialize + Send + 'static, 162 | { 163 | self.add_typed_route( 164 | ipc_receiver, 165 | Box::new(move |message| drop(crossbeam_sender.send(message.unwrap()))), 166 | ) 167 | } 168 | 169 | /// A convenience function to route an `IpcReceiver` to a `Receiver`: the most common 170 | /// use of a `Router`. 171 | pub fn route_ipc_receiver_to_new_crossbeam_receiver( 172 | &self, 173 | ipc_receiver: IpcReceiver, 174 | ) -> Receiver 175 | where 176 | T: for<'de> Deserialize<'de> + Serialize + Send + 'static, 177 | { 178 | let (crossbeam_sender, crossbeam_receiver) = crossbeam_channel::unbounded(); 179 | self.route_ipc_receiver_to_crossbeam_sender(ipc_receiver, crossbeam_sender); 180 | crossbeam_receiver 181 | } 182 | } 183 | 184 | struct RouterProxyComm { 185 | msg_sender: Sender, 186 | wakeup_sender: IpcSender<()>, 187 | shutdown: bool, 188 | handle: Option>, 189 | } 190 | 191 | /// Router runs in its own thread listening for events. Adds events to its IpcReceiverSet 192 | /// and listens for events using select(). 193 | struct Router { 194 | /// Get messages from RouterProxy. 195 | msg_receiver: Receiver, 196 | /// The ID/index of the special channel we use to identify messages from msg_receiver. 197 | msg_wakeup_id: u64, 198 | /// Set of all receivers which have been registered for us to select on. 199 | ipc_receiver_set: IpcReceiverSet, 200 | /// Maps ids to their handler functions. 201 | handlers: HashMap, 202 | } 203 | 204 | impl Router { 205 | fn new(msg_receiver: Receiver, wakeup_receiver: IpcReceiver<()>) -> Router { 206 | let mut ipc_receiver_set = IpcReceiverSet::new().unwrap(); 207 | let msg_wakeup_id = ipc_receiver_set.add(wakeup_receiver).unwrap(); 208 | Router { 209 | msg_receiver, 210 | msg_wakeup_id, 211 | ipc_receiver_set, 212 | handlers: HashMap::new(), 213 | } 214 | } 215 | 216 | /// Continuously loop waiting for wakeup signals from router proxy. 217 | /// Iterate over events either: 218 | /// 1) If a message comes in from our special `wakeup_receiver` (identified through 219 | /// msg_wakeup_id. Read message from `msg_receiver` and add a new receiver 220 | /// to our receiver set. 221 | /// 2) Call appropriate handler based on message id. 222 | /// 3) Remove handler once channel closes. 223 | fn run(&mut self) { 224 | loop { 225 | // Wait for events to come from our select() new channels are added to 226 | // our ReceiverSet below. 227 | let results = match self.ipc_receiver_set.select() { 228 | Ok(results) => results, 229 | Err(_) => break, 230 | }; 231 | 232 | // Iterate over numerous events that were ready at this time. 233 | for result in results.into_iter() { 234 | match result { 235 | // Message came from the RouterProxy. Listen on our `msg_receiver` 236 | // channel. 237 | IpcSelectionResult::MessageReceived(id, _) if id == self.msg_wakeup_id => { 238 | match self.msg_receiver.recv().unwrap() { 239 | RouterMsg::AddRoute(receiver, handler) => { 240 | let new_receiver_id = 241 | self.ipc_receiver_set.add_opaque(receiver).unwrap(); 242 | self.handlers.insert(new_receiver_id, handler); 243 | }, 244 | RouterMsg::Shutdown(sender) => { 245 | sender 246 | .send(()) 247 | .expect("Failed to send comfirmation of shutdown."); 248 | return; 249 | }, 250 | } 251 | }, 252 | // Event from one of our registered receivers, call callback. 253 | IpcSelectionResult::MessageReceived(id, message) => { 254 | match self.handlers.get_mut(&id).unwrap() { 255 | RouterHandler::Once(handler) => { 256 | if let Some(handler) = handler.take() { 257 | (handler)(message); 258 | } 259 | }, 260 | RouterHandler::Multi(ref mut handler) => { 261 | (handler)(message); 262 | }, 263 | } 264 | }, 265 | IpcSelectionResult::ChannelClosed(id) => { 266 | let _ = self.handlers.remove(&id).unwrap(); 267 | }, 268 | } 269 | } 270 | } 271 | } 272 | } 273 | 274 | enum RouterMsg { 275 | /// Register the receiver OpaqueIpcReceiver for listening for events on. 276 | /// When a message comes from this receiver, call RouterHandler. 277 | AddRoute(OpaqueIpcReceiver, RouterHandler), 278 | /// Shutdown the router, providing a sender to send an acknowledgement. 279 | Shutdown(Sender<()>), 280 | } 281 | 282 | /// Function to call when a new event is received from the corresponding receiver. 283 | pub type RouterMultiHandler = Box; 284 | 285 | /// Function to call the first time that a message is received from the corresponding receiver. 286 | pub type RouterOneShotHandler = Box; 287 | 288 | enum RouterHandler { 289 | Once(Option), 290 | Multi(RouterMultiHandler), 291 | } 292 | 293 | /// Like [RouterMultiHandler] but includes the type that will be passed to the callback 294 | pub type TypedRouterMultiHandler = Box) + Send>; 295 | 296 | /// Like [RouterOneShotHandler] but includes the type that will be passed to the callback 297 | pub type TypedRouterOneShotHandler = Box) + Send>; 298 | -------------------------------------------------------------------------------- /src/test.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The Servo Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution. 3 | // 4 | // Licensed under the Apache License, Version 2.0 or the MIT license 6 | // , at your 7 | // option. This file may not be copied, modified, or distributed 8 | // except according to those terms. 9 | 10 | use std::cell::RefCell; 11 | #[cfg(not(any(feature = "force-inprocess", target_os = "android", target_os = "ios")))] 12 | use std::env; 13 | #[cfg(not(any( 14 | feature = "force-inprocess", 15 | target_os = "android", 16 | target_os = "ios", 17 | target_os = "windows", 18 | )))] 19 | use std::io::Error; 20 | #[cfg(not(any(feature = "force-inprocess", target_os = "android", target_os = "ios",)))] 21 | use std::process::{self, Command, Stdio}; 22 | #[cfg(not(any( 23 | feature = "force-inprocess", 24 | target_os = "android", 25 | target_os = "ios", 26 | target_os = "windows", 27 | )))] 28 | use std::ptr; 29 | use std::rc::Rc; 30 | use std::time::{Duration, Instant}; 31 | use std::{iter, thread}; 32 | 33 | use crossbeam_channel::{self, Sender}; 34 | use serde::{Deserialize, Deserializer, Serialize, Serializer}; 35 | 36 | #[cfg(not(any( 37 | feature = "force-inprocess", 38 | target_os = "android", 39 | target_os = "ios", 40 | target_os = "windows" 41 | )))] 42 | use crate::ipc::IpcOneShotServer; 43 | #[cfg(not(any(feature = "force-inprocess", target_os = "android", target_os = "ios")))] 44 | use crate::ipc::IpcReceiver; 45 | use crate::ipc::{self, IpcReceiverSet, IpcSender, IpcSharedMemory}; 46 | use crate::router::{RouterProxy, ROUTER}; 47 | 48 | #[cfg(not(any( 49 | feature = "force-inprocess", 50 | target_os = "windows", 51 | target_os = "android", 52 | target_os = "ios" 53 | )))] 54 | // I'm not actually sure invoking this is indeed unsafe -- but better safe than sorry... 55 | pub unsafe fn fork(child_func: F) -> libc::pid_t { 56 | match libc::fork() { 57 | -1 => panic!("Fork failed: {}", Error::last_os_error()), 58 | 0 => { 59 | child_func(); 60 | libc::exit(0); 61 | }, 62 | pid => pid, 63 | } 64 | } 65 | 66 | #[cfg(not(any( 67 | feature = "force-inprocess", 68 | target_os = "windows", 69 | target_os = "android", 70 | target_os = "ios" 71 | )))] 72 | pub trait Wait { 73 | fn wait(self); 74 | } 75 | 76 | #[cfg(not(any( 77 | feature = "force-inprocess", 78 | target_os = "windows", 79 | target_os = "android", 80 | target_os = "ios" 81 | )))] 82 | impl Wait for libc::pid_t { 83 | fn wait(self) { 84 | unsafe { 85 | libc::waitpid(self, ptr::null_mut(), 0); 86 | } 87 | } 88 | } 89 | 90 | // Helper to get a channel_name argument passed in; used for the 91 | // cross-process spawn server tests. 92 | #[cfg(not(any(feature = "force-inprocess", target_os = "android", target_os = "ios")))] 93 | pub fn get_channel_name_arg(which: &str) -> Option { 94 | for arg in env::args() { 95 | let arg_str = &*format!("channel_name-{which}:"); 96 | if let Some(arg) = arg.strip_prefix(arg_str) { 97 | return Some(arg.to_owned()); 98 | } 99 | } 100 | None 101 | } 102 | 103 | // Helper to get a channel_name argument passed in; used for the 104 | // cross-process spawn server tests. 105 | #[cfg(not(any(feature = "force-inprocess", target_os = "android", target_os = "ios",)))] 106 | pub fn spawn_server(test_name: &str, server_args: &[(&str, &str)]) -> process::Child { 107 | Command::new(env::current_exe().unwrap()) 108 | .arg(test_name) 109 | .args( 110 | server_args 111 | .iter() 112 | .map(|(name, val)| format!("channel_name-{name}:{val}")), 113 | ) 114 | .stdin(Stdio::null()) 115 | .stdout(Stdio::null()) 116 | .stderr(Stdio::null()) 117 | .spawn() 118 | .expect("failed to execute server process") 119 | } 120 | 121 | type Person = (String, u32); 122 | 123 | #[test] 124 | fn simple() { 125 | let person = ("Patrick Walton".to_owned(), 29); 126 | let (tx, rx) = ipc::channel().unwrap(); 127 | tx.send(person.clone()).unwrap(); 128 | let received_person = rx.recv().unwrap(); 129 | assert_eq!(person, received_person); 130 | drop(tx); 131 | match rx.recv().unwrap_err() { 132 | ipc::IpcError::Disconnected => (), 133 | e => panic!("expected disconnected error, got {e:?}"), 134 | } 135 | } 136 | 137 | #[test] 138 | fn embedded_senders() { 139 | let person = ("Patrick Walton".to_owned(), 29); 140 | let (sub_tx, sub_rx) = ipc::channel().unwrap(); 141 | let person_and_sender = (person.clone(), sub_tx); 142 | let (super_tx, super_rx) = ipc::channel().unwrap(); 143 | super_tx.send(person_and_sender).unwrap(); 144 | let received_person_and_sender = super_rx.recv().unwrap(); 145 | assert_eq!(received_person_and_sender.0, person); 146 | received_person_and_sender.1.send(person.clone()).unwrap(); 147 | let received_person = sub_rx.recv().unwrap(); 148 | assert_eq!(received_person, person); 149 | } 150 | 151 | #[test] 152 | fn embedded_receivers() { 153 | let person = ("Patrick Walton".to_owned(), 29); 154 | let (sub_tx, sub_rx) = ipc::channel().unwrap(); 155 | let person_and_receiver = (person.clone(), sub_rx); 156 | let (super_tx, super_rx) = ipc::channel().unwrap(); 157 | super_tx.send(person_and_receiver).unwrap(); 158 | let received_person_and_receiver = super_rx.recv().unwrap(); 159 | assert_eq!(received_person_and_receiver.0, person); 160 | sub_tx.send(person.clone()).unwrap(); 161 | let received_person = received_person_and_receiver.1.recv().unwrap(); 162 | assert_eq!(received_person, person); 163 | } 164 | 165 | #[test] 166 | fn select() { 167 | let (tx0, rx0) = ipc::channel().unwrap(); 168 | let (tx1, rx1) = ipc::channel().unwrap(); 169 | let mut rx_set = IpcReceiverSet::new().unwrap(); 170 | let rx0_id = rx_set.add(rx0).unwrap(); 171 | let rx1_id = rx_set.add(rx1).unwrap(); 172 | 173 | let person = ("Patrick Walton".to_owned(), 29); 174 | tx0.send(person.clone()).unwrap(); 175 | let (received_id, received_data) = rx_set 176 | .select() 177 | .unwrap() 178 | .into_iter() 179 | .next() 180 | .unwrap() 181 | .unwrap(); 182 | let received_person: Person = received_data.to().unwrap(); 183 | assert_eq!(received_id, rx0_id); 184 | assert_eq!(received_person, person); 185 | 186 | tx1.send(person.clone()).unwrap(); 187 | let (received_id, received_data) = rx_set 188 | .select() 189 | .unwrap() 190 | .into_iter() 191 | .next() 192 | .unwrap() 193 | .unwrap(); 194 | let received_person: Person = received_data.to().unwrap(); 195 | assert_eq!(received_id, rx1_id); 196 | assert_eq!(received_person, person); 197 | 198 | tx0.send(person.clone()).unwrap(); 199 | tx1.send(person.clone()).unwrap(); 200 | let (mut received0, mut received1) = (false, false); 201 | while !received0 || !received1 { 202 | for result in rx_set.select().unwrap().into_iter() { 203 | let (received_id, received_data) = result.unwrap(); 204 | let received_person: Person = received_data.to().unwrap(); 205 | assert_eq!(received_person, person); 206 | assert!(received_id == rx0_id || received_id == rx1_id); 207 | if received_id == rx0_id { 208 | assert!(!received0); 209 | received0 = true; 210 | } else if received_id == rx1_id { 211 | assert!(!received1); 212 | received1 = true; 213 | } 214 | } 215 | } 216 | } 217 | 218 | #[cfg(not(any(feature = "force-inprocess", target_os = "android", target_os = "ios")))] 219 | #[test] 220 | fn cross_process_embedded_senders_spawn() { 221 | let person = ("Patrick Walton".to_owned(), 29); 222 | 223 | let server0_name = get_channel_name_arg("server0"); 224 | let server2_name = get_channel_name_arg("server2"); 225 | if let (Some(server0_name), Some(server2_name)) = (server0_name, server2_name) { 226 | let (tx1, rx1): (IpcSender, IpcReceiver) = ipc::channel().unwrap(); 227 | let tx0 = IpcSender::connect(server0_name).unwrap(); 228 | tx0.send(tx1).unwrap(); 229 | rx1.recv().unwrap(); 230 | let tx2: IpcSender = IpcSender::connect(server2_name).unwrap(); 231 | tx2.send(person.clone()).unwrap(); 232 | 233 | unsafe { 234 | libc::exit(0); 235 | } 236 | } 237 | } 238 | 239 | #[cfg(not(any( 240 | feature = "force-inprocess", 241 | target_os = "windows", 242 | target_os = "android", 243 | target_os = "ios" 244 | )))] 245 | #[test] 246 | fn cross_process_embedded_senders_fork() { 247 | let person = ("Patrick Walton".to_owned(), 29); 248 | let (server0, server0_name) = IpcOneShotServer::new().unwrap(); 249 | let (server2, server2_name) = IpcOneShotServer::new().unwrap(); 250 | let child_pid = unsafe { 251 | fork(|| { 252 | let (tx1, rx1): (IpcSender, IpcReceiver) = ipc::channel().unwrap(); 253 | let tx0 = IpcSender::connect(server0_name).unwrap(); 254 | tx0.send(tx1).unwrap(); 255 | rx1.recv().unwrap(); 256 | let tx2: IpcSender = IpcSender::connect(server2_name).unwrap(); 257 | tx2.send(person.clone()).unwrap(); 258 | }) 259 | }; 260 | let (_, tx1): (_, IpcSender) = server0.accept().unwrap(); 261 | tx1.send(person.clone()).unwrap(); 262 | let (_, received_person): (_, Person) = server2.accept().unwrap(); 263 | child_pid.wait(); 264 | assert_eq!(received_person, person); 265 | } 266 | 267 | #[test] 268 | fn router_simple_global() { 269 | // Note: All ROUTER operation need to run in a single test, 270 | // since the state of the router will carry across tests. 271 | 272 | let person = ("Patrick Walton".to_owned(), 29); 273 | let (tx, rx) = ipc::channel().unwrap(); 274 | tx.send(person.clone()).unwrap(); 275 | 276 | let (callback_fired_sender, callback_fired_receiver) = crossbeam_channel::unbounded::(); 277 | ROUTER.add_typed_route( 278 | rx, 279 | Box::new(move |person| { 280 | callback_fired_sender.send(person.unwrap()).unwrap(); 281 | }), 282 | ); 283 | let received_person = callback_fired_receiver.recv().unwrap(); 284 | assert_eq!(received_person, person); 285 | 286 | // Try the same, with a strongly typed route 287 | let message: usize = 42; 288 | let (tx, rx) = ipc::channel().unwrap(); 289 | tx.send(message).unwrap(); 290 | 291 | let (callback_fired_sender, callback_fired_receiver) = crossbeam_channel::unbounded::(); 292 | ROUTER.add_typed_route( 293 | rx, 294 | Box::new(move |message| { 295 | callback_fired_sender.send(message.unwrap()).unwrap(); 296 | }), 297 | ); 298 | let received_message = callback_fired_receiver.recv().unwrap(); 299 | assert_eq!(received_message, message); 300 | 301 | // Now shutdown the router. 302 | ROUTER.shutdown(); 303 | 304 | // Use router after shutdown. 305 | let person = ("Patrick Walton".to_owned(), 29); 306 | let (tx, rx) = ipc::channel().unwrap(); 307 | tx.send(person.clone()).unwrap(); 308 | 309 | let (callback_fired_sender, callback_fired_receiver) = crossbeam_channel::unbounded::(); 310 | ROUTER.add_typed_route( 311 | rx, 312 | Box::new(move |person| { 313 | callback_fired_sender.send(person.unwrap()).unwrap(); 314 | }), 315 | ); 316 | 317 | // The sender should have been dropped. 318 | let received_person = callback_fired_receiver.recv(); 319 | assert!(received_person.is_err()); 320 | 321 | // Shutdown the router, again(should be a no-op). 322 | ROUTER.shutdown(); 323 | } 324 | 325 | #[cfg_attr(not(feature = "enable-slow-tests"), ignore)] 326 | #[test] 327 | fn router_flood() { 328 | let router = RouterProxy::new(); 329 | for _ in 0..1_000_000 { 330 | let person = ("Patrick Walton".to_owned(), 29); 331 | let (tx, rx) = ipc::channel().unwrap(); 332 | let _ = tx.send(person.clone()); 333 | 334 | let (tx2, rx2) = ipc::channel().unwrap(); 335 | router.add_typed_route(rx, Box::new(move |msg| drop(tx2.send(msg.unwrap())))); 336 | let received_person = rx2.recv().unwrap(); 337 | assert_eq!(received_person, person); 338 | } 339 | } 340 | 341 | #[test] 342 | fn router_shutdown() { 343 | let router = RouterProxy::new(); 344 | router.shutdown(); 345 | } 346 | 347 | #[test] 348 | fn router_routing_to_new_crossbeam_receiver() { 349 | let person = ("Patrick Walton".to_owned(), 29); 350 | let (tx, rx) = ipc::channel().unwrap(); 351 | tx.send(person.clone()).unwrap(); 352 | 353 | let router = RouterProxy::new(); 354 | let crossbeam_receiver = router.route_ipc_receiver_to_new_crossbeam_receiver(rx); 355 | let received_person = crossbeam_receiver.recv().unwrap(); 356 | assert_eq!(received_person, person); 357 | } 358 | 359 | #[test] 360 | fn router_once_handler() { 361 | let person = ("Patrick Walton".to_owned(), 29); 362 | let (tx, rx) = ipc::channel().unwrap(); 363 | let (tx2, rx2) = ipc::channel().unwrap(); 364 | 365 | let router = RouterProxy::new(); 366 | let mut once_tx2 = Some(tx2); 367 | router.add_typed_one_shot_route( 368 | rx, 369 | Box::new(move |_msg| once_tx2.take().unwrap().send(()).unwrap()), 370 | ); 371 | 372 | // Send one single event. 373 | tx.send(person.clone()).unwrap(); 374 | // Wait for acknowledgement that the callback ran. 375 | rx2.recv().unwrap(); 376 | // This send should succeed but no handler should run. If it does run, 377 | // a panic will occur. 378 | tx.send(person.clone()).unwrap(); 379 | assert!(rx2.recv().is_err()); 380 | } 381 | 382 | #[test] 383 | fn router_multiplexing() { 384 | let person = ("Patrick Walton".to_owned(), 29); 385 | let (tx0, rx0) = ipc::channel().unwrap(); 386 | tx0.send(person.clone()).unwrap(); 387 | let (tx1, rx1) = ipc::channel().unwrap(); 388 | tx1.send(person.clone()).unwrap(); 389 | 390 | let router = RouterProxy::new(); 391 | let crossbeam_rx_0 = router.route_ipc_receiver_to_new_crossbeam_receiver(rx0); 392 | let crossbeam_rx_1 = router.route_ipc_receiver_to_new_crossbeam_receiver(rx1); 393 | let received_person_0 = crossbeam_rx_0.recv().unwrap(); 394 | let received_person_1 = crossbeam_rx_1.recv().unwrap(); 395 | assert_eq!(received_person_0, person); 396 | assert_eq!(received_person_1, person); 397 | } 398 | 399 | #[test] 400 | fn router_multithreaded_multiplexing() { 401 | let person = ("Patrick Walton".to_owned(), 29); 402 | 403 | let person_for_thread = person.clone(); 404 | let (tx0, rx0) = ipc::channel().unwrap(); 405 | thread::spawn(move || tx0.send(person_for_thread).unwrap()); 406 | let person_for_thread = person.clone(); 407 | let (tx1, rx1) = ipc::channel().unwrap(); 408 | thread::spawn(move || tx1.send(person_for_thread).unwrap()); 409 | 410 | let router = RouterProxy::new(); 411 | let crossbeam_rx_0 = router.route_ipc_receiver_to_new_crossbeam_receiver(rx0); 412 | let crossbeam_rx_1 = router.route_ipc_receiver_to_new_crossbeam_receiver(rx1); 413 | let received_person_0 = crossbeam_rx_0.recv().unwrap(); 414 | let received_person_1 = crossbeam_rx_1.recv().unwrap(); 415 | assert_eq!(received_person_0, person); 416 | assert_eq!(received_person_1, person); 417 | } 418 | 419 | #[test] 420 | fn router_drops_callbacks_on_sender_shutdown() { 421 | struct Dropper { 422 | sender: Sender, 423 | } 424 | 425 | impl Drop for Dropper { 426 | fn drop(&mut self) { 427 | self.sender.send(42).unwrap(); 428 | } 429 | } 430 | 431 | let (tx0, rx0) = ipc::channel::<()>().unwrap(); 432 | let (drop_tx, drop_rx) = crossbeam_channel::unbounded(); 433 | let dropper = Dropper { sender: drop_tx }; 434 | 435 | let router = RouterProxy::new(); 436 | router.add_typed_route( 437 | rx0, 438 | Box::new(move |_| { 439 | let _ = &dropper; 440 | }), 441 | ); 442 | drop(tx0); 443 | assert_eq!(drop_rx.recv(), Ok(42)); 444 | } 445 | 446 | #[test] 447 | fn router_drops_callbacks_on_cloned_sender_shutdown() { 448 | struct Dropper { 449 | sender: Sender, 450 | } 451 | 452 | impl Drop for Dropper { 453 | fn drop(&mut self) { 454 | self.sender.send(42).unwrap() 455 | } 456 | } 457 | 458 | let (tx0, rx0) = ipc::channel::<()>().unwrap(); 459 | let (drop_tx, drop_rx) = crossbeam_channel::unbounded(); 460 | let dropper = Dropper { sender: drop_tx }; 461 | 462 | let router = RouterProxy::new(); 463 | router.add_typed_route( 464 | rx0, 465 | Box::new(move |_| { 466 | let _ = &dropper; 467 | }), 468 | ); 469 | let txs = vec![tx0.clone(), tx0.clone(), tx0.clone()]; 470 | drop(txs); 471 | drop(tx0); 472 | assert_eq!(drop_rx.recv(), Ok(42)); 473 | } 474 | 475 | #[test] 476 | fn router_big_data() { 477 | let person = ("Patrick Walton".to_owned(), 29); 478 | let people: Vec<_> = std::iter::repeat_n(person, 64 * 1024).collect(); 479 | let (tx, rx) = ipc::channel().unwrap(); 480 | let people_for_subthread = people.clone(); 481 | let thread = thread::spawn(move || { 482 | tx.send(people_for_subthread).unwrap(); 483 | }); 484 | 485 | let (callback_fired_sender, callback_fired_receiver) = 486 | crossbeam_channel::unbounded::>(); 487 | let router = RouterProxy::new(); 488 | router.add_typed_route( 489 | rx, 490 | Box::new(move |people| callback_fired_sender.send(people.unwrap()).unwrap()), 491 | ); 492 | let received_people = callback_fired_receiver.recv().unwrap(); 493 | assert_eq!(received_people, people); 494 | thread.join().unwrap(); 495 | } 496 | 497 | #[test] 498 | fn shared_memory() { 499 | let person = ("Patrick Walton".to_owned(), 29); 500 | let person_and_shared_memory = (person, IpcSharedMemory::from_byte(0xba, 1024 * 1024)); 501 | let (tx, rx) = ipc::channel().unwrap(); 502 | tx.send(person_and_shared_memory.clone()).unwrap(); 503 | let received_person_and_shared_memory = rx.recv().unwrap(); 504 | assert_eq!( 505 | received_person_and_shared_memory.0, 506 | person_and_shared_memory.0 507 | ); 508 | assert!(person_and_shared_memory.1.iter().all(|byte| *byte == 0xba)); 509 | assert!(received_person_and_shared_memory 510 | .1 511 | .iter() 512 | .all(|byte| *byte == 0xba)); 513 | } 514 | 515 | #[test] 516 | fn shared_memory_slice() { 517 | let (tx, rx) = ipc::channel().unwrap(); 518 | // test byte of size 0 519 | let shared_memory = IpcSharedMemory::from_byte(42, 0); 520 | tx.send(shared_memory.clone()).unwrap(); 521 | let received_shared_memory = rx.recv().unwrap(); 522 | assert_eq!(*received_shared_memory, *shared_memory); 523 | // test empty slice 524 | let shared_memory = IpcSharedMemory::from_bytes(&[]); 525 | tx.send(shared_memory.clone()).unwrap(); 526 | let received_shared_memory = rx.recv().unwrap(); 527 | assert_eq!(*received_shared_memory, *shared_memory); 528 | // test non-empty slice 529 | let shared_memory = IpcSharedMemory::from_bytes(&[4, 2, 42]); 530 | tx.send(shared_memory.clone()).unwrap(); 531 | let received_shared_memory = rx.recv().unwrap(); 532 | assert_eq!(*received_shared_memory, *shared_memory); 533 | } 534 | 535 | #[test] 536 | fn shared_memory_object_equality() { 537 | let person = ("Patrick Walton".to_owned(), 29); 538 | let person_and_shared_memory = (person, IpcSharedMemory::from_byte(0xba, 1024 * 1024)); 539 | let (tx, rx) = ipc::channel().unwrap(); 540 | tx.send(person_and_shared_memory.clone()).unwrap(); 541 | let received_person_and_shared_memory = rx.recv().unwrap(); 542 | assert_eq!(received_person_and_shared_memory, person_and_shared_memory); 543 | } 544 | 545 | #[test] 546 | fn opaque_sender() { 547 | let person = ("Patrick Walton".to_owned(), 29); 548 | let (tx, rx) = ipc::channel().unwrap(); 549 | let opaque_tx = tx.to_opaque(); 550 | let tx: IpcSender = opaque_tx.to(); 551 | tx.send(person.clone()).unwrap(); 552 | let received_person = rx.recv().unwrap(); 553 | assert_eq!(person, received_person); 554 | } 555 | 556 | #[test] 557 | fn embedded_opaque_senders() { 558 | let person = ("Patrick Walton".to_owned(), 29); 559 | let (sub_tx, sub_rx) = ipc::channel::().unwrap(); 560 | let person_and_sender = (person.clone(), sub_tx.to_opaque()); 561 | let (super_tx, super_rx) = ipc::channel().unwrap(); 562 | super_tx.send(person_and_sender).unwrap(); 563 | let received_person_and_sender = super_rx.recv().unwrap(); 564 | assert_eq!(received_person_and_sender.0, person); 565 | received_person_and_sender 566 | .1 567 | .to::() 568 | .send(person.clone()) 569 | .unwrap(); 570 | let received_person = sub_rx.recv().unwrap(); 571 | assert_eq!(received_person, person); 572 | } 573 | 574 | #[test] 575 | fn try_recv() { 576 | let person = ("Patrick Walton".to_owned(), 29); 577 | let (tx, rx) = ipc::channel().unwrap(); 578 | match rx.try_recv() { 579 | Err(ipc::TryRecvError::Empty) => (), 580 | v => panic!("Expected empty channel err: {v:?}"), 581 | } 582 | tx.send(person.clone()).unwrap(); 583 | let received_person = rx.try_recv().unwrap(); 584 | assert_eq!(person, received_person); 585 | match rx.try_recv() { 586 | Err(ipc::TryRecvError::Empty) => (), 587 | v => panic!("Expected empty channel err: {v:?}"), 588 | } 589 | drop(tx); 590 | match rx.try_recv() { 591 | Err(ipc::TryRecvError::IpcError(ipc::IpcError::Disconnected)) => (), 592 | v => panic!("Expected disconnected err: {v:?}"), 593 | } 594 | } 595 | 596 | #[test] 597 | fn try_recv_timeout() { 598 | let person = ("Jacob Kiesel".to_owned(), 25); 599 | let (tx, rx) = ipc::channel().unwrap(); 600 | let timeout = Duration::from_millis(1000); 601 | let start_recv = Instant::now(); 602 | match rx.try_recv_timeout(timeout) { 603 | Err(ipc::TryRecvError::Empty) => { 604 | assert!(start_recv.elapsed() >= Duration::from_millis(500)) 605 | }, 606 | v => panic!("Expected empty channel err: {v:?}"), 607 | } 608 | tx.send(person.clone()).unwrap(); 609 | let start_recv = Instant::now(); 610 | let received_person = rx.try_recv_timeout(timeout).unwrap(); 611 | assert!(start_recv.elapsed() < timeout); 612 | assert_eq!(person, received_person); 613 | let start_recv = Instant::now(); 614 | match rx.try_recv_timeout(timeout) { 615 | Err(ipc::TryRecvError::Empty) => { 616 | assert!(start_recv.elapsed() >= Duration::from_millis(500)) 617 | }, 618 | v => panic!("Expected empty channel err: {v:?}"), 619 | } 620 | drop(tx); 621 | match rx.try_recv_timeout(timeout) { 622 | Err(ipc::TryRecvError::IpcError(ipc::IpcError::Disconnected)) => (), 623 | v => panic!("Expected disconnected err: {v:?}"), 624 | } 625 | } 626 | 627 | #[test] 628 | fn multiple_paths_to_a_sender() { 629 | let person = ("Patrick Walton".to_owned(), 29); 630 | let (sub_tx, sub_rx) = ipc::channel().unwrap(); 631 | let person_and_sender = Rc::new((person.clone(), sub_tx)); 632 | let send_data = vec![ 633 | person_and_sender.clone(), 634 | person_and_sender.clone(), 635 | person_and_sender.clone(), 636 | ]; 637 | let (super_tx, super_rx) = ipc::channel().unwrap(); 638 | super_tx.send(send_data).unwrap(); 639 | let received_data = super_rx.recv().unwrap(); 640 | assert_eq!(received_data[0].0, person); 641 | assert_eq!(received_data[1].0, person); 642 | assert_eq!(received_data[2].0, person); 643 | received_data[0].1.send(person.clone()).unwrap(); 644 | let received_person = sub_rx.recv().unwrap(); 645 | assert_eq!(received_person, person); 646 | received_data[1].1.send(person.clone()).unwrap(); 647 | let received_person = sub_rx.recv().unwrap(); 648 | assert_eq!(received_person, person); 649 | } 650 | 651 | #[test] 652 | fn bytes() { 653 | // N.B. We're using an odd number of bytes here to expose alignment issues. 654 | let bytes = [1, 2, 3, 4, 5, 6, 7]; 655 | let (tx, rx) = ipc::bytes_channel().unwrap(); 656 | tx.send(&bytes[..]).unwrap(); 657 | let received_bytes = rx.recv().unwrap(); 658 | assert_eq!(&bytes, &received_bytes[..]); 659 | } 660 | 661 | #[test] 662 | fn embedded_bytes_receivers() { 663 | let (sub_tx, sub_rx) = ipc::bytes_channel().unwrap(); 664 | let (super_tx, super_rx) = ipc::channel().unwrap(); 665 | super_tx.send(sub_tx).unwrap(); 666 | let sub_tx = super_rx.recv().unwrap(); 667 | let bytes = [1, 2, 3, 4, 5, 6, 7]; 668 | sub_tx.send(&bytes[..]).unwrap(); 669 | let received_bytes = sub_rx.recv().unwrap(); 670 | assert_eq!(&bytes, &received_bytes[..]); 671 | } 672 | 673 | #[test] 674 | fn test_so_linger() { 675 | let (sender, receiver) = ipc::channel().unwrap(); 676 | sender.send(42).unwrap(); 677 | drop(sender); 678 | let val = match receiver.recv() { 679 | Ok(val) => val, 680 | Err(e) => { 681 | panic!("err: `{e:?}`"); 682 | }, 683 | }; 684 | assert_eq!(val, 42); 685 | } 686 | 687 | #[derive(Clone, Debug, Eq, PartialEq)] 688 | struct HasWeirdSerializer(Option); 689 | 690 | thread_local! { static WEIRD_CHANNEL: RefCell>> = const { RefCell::new(None) } } 691 | 692 | impl Serialize for HasWeirdSerializer { 693 | fn serialize(&self, serializer: S) -> Result 694 | where 695 | S: Serializer, 696 | { 697 | if self.0.is_some() { 698 | WEIRD_CHANNEL.with(|chan| { 699 | chan.borrow() 700 | .as_ref() 701 | .unwrap() 702 | .send(HasWeirdSerializer(None)) 703 | .unwrap(); 704 | }); 705 | } 706 | self.0.serialize(serializer) 707 | } 708 | } 709 | 710 | impl<'de> Deserialize<'de> for HasWeirdSerializer { 711 | fn deserialize(deserializer: D) -> Result 712 | where 713 | D: Deserializer<'de>, 714 | { 715 | Ok(HasWeirdSerializer(Deserialize::deserialize(deserializer)?)) 716 | } 717 | } 718 | 719 | #[test] 720 | fn test_reentrant() { 721 | let null = HasWeirdSerializer(None); 722 | let hello = HasWeirdSerializer(Some(String::from("hello"))); 723 | let (sender, receiver) = ipc::channel().unwrap(); 724 | WEIRD_CHANNEL.with(|chan| { 725 | *chan.borrow_mut() = Some(sender.clone()); 726 | }); 727 | sender.send(hello.clone()).unwrap(); 728 | assert_eq!(null, receiver.recv().unwrap()); 729 | assert_eq!(hello, receiver.recv().unwrap()); 730 | sender.send(null.clone()).unwrap(); 731 | assert_eq!(null, receiver.recv().unwrap()); 732 | } 733 | 734 | #[test] 735 | fn clone_sender_after_receiver_dropped() { 736 | let (tx, rx) = ipc::channel::().unwrap(); 737 | drop(rx); 738 | let _tx2 = tx.clone(); 739 | } 740 | 741 | #[test] 742 | fn transfer_closed_sender() { 743 | let (main_tx, main_rx) = ipc::channel().unwrap(); 744 | let (transfer_tx, _) = ipc::channel::<()>().unwrap(); 745 | assert!(main_tx.send(transfer_tx).is_ok()); 746 | let _transferred_tx = main_rx.recv().unwrap(); 747 | } 748 | 749 | #[cfg(feature = "async")] 750 | #[test] 751 | fn test_receiver_stream() { 752 | use std::pin::Pin; 753 | 754 | use futures_core::task::{Context, Poll}; 755 | use futures_core::Stream; 756 | let (tx, rx) = ipc::channel().unwrap(); 757 | let (waker, count) = futures_test::task::new_count_waker(); 758 | let mut ctx = Context::from_waker(&waker); 759 | let mut stream = rx.to_stream(); 760 | 761 | assert_eq!(count, 0); 762 | match Pin::new(&mut stream).poll_next(&mut ctx) { 763 | Poll::Pending => (), 764 | _ => panic!("Stream shouldn't have data"), 765 | }; 766 | assert_eq!(count, 0); 767 | tx.send(5).unwrap(); 768 | thread::sleep(std::time::Duration::from_millis(1000)); 769 | assert_eq!(count, 1); 770 | match Pin::new(&mut stream).poll_next(&mut ctx) { 771 | Poll::Ready(Some(Ok(5))) => (), 772 | _ => panic!("Stream should have 5"), 773 | }; 774 | } 775 | 776 | mod sync_test { 777 | use crate::ipc::{IpcReceiver, IpcSender}; 778 | use static_assertions::{assert_impl_all, assert_not_impl_any}; 779 | 780 | #[test] 781 | fn ipc_receiver_not_sync() { 782 | // A single-consumer queue is not Sync. 783 | assert_not_impl_any!(IpcReceiver : Sync); 784 | } 785 | 786 | #[test] 787 | fn ipc_sender_is_sync() { 788 | // A multi-producer queue must be Sync. 789 | assert_impl_all!(IpcSender : Sync); 790 | } 791 | } 792 | -------------------------------------------------------------------------------- /tests/integration_test.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2025 The Servo Project Developers. See the COPYRIGHT 2 | // file at the top-level directory of this distribution. 3 | // 4 | // Licensed under the Apache License, Version 2.0 or the MIT license 6 | // , at your 7 | // option. This file may not be copied, modified, or distributed 8 | // except according to those terms. 9 | 10 | #[cfg(not(any(feature = "force-inprocess", target_os = "android", target_os = "ios")))] 11 | use ipc_channel::ipc::IpcOneShotServer; 12 | #[cfg(not(any(feature = "force-inprocess", target_os = "android", target_os = "ios")))] 13 | use std::{env, process}; 14 | 15 | // These integration tests may be run on their own by issuing: 16 | // cargo test --test '*' 17 | 18 | /// Test spawning a process which then acts as a client to a 19 | /// one-shot server in the parent process. 20 | #[cfg(not(any(feature = "force-inprocess", target_os = "android", target_os = "ios")))] 21 | #[test] 22 | fn spawn_one_shot_server_client() { 23 | let executable_path: String = env!("CARGO_BIN_EXE_spawn_client_test_helper").to_string(); 24 | 25 | let (server, token) = 26 | IpcOneShotServer::::new().expect("Failed to create IPC one-shot server."); 27 | 28 | let mut command = process::Command::new(executable_path); 29 | let child_process = command.arg(token); 30 | 31 | let mut child = child_process 32 | .spawn() 33 | .expect("Failed to start child process"); 34 | 35 | let (_rx, msg) = server.accept().expect("accept failed"); 36 | assert_eq!("test message", msg); 37 | 38 | let result = child.wait().expect("wait for child process failed"); 39 | assert!( 40 | result.success(), 41 | "child process failed with exit status code {}", 42 | result.code().expect("exit status code not available") 43 | ); 44 | } 45 | --------------------------------------------------------------------------------