├── .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 ├── 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 | 34 | - name: rustfmt 35 | run: cargo fmt --check 36 | 37 | - name: clippy 38 | run: cargo clippy --features "${{ matrix.features }}" --target ${{ matrix.platform.target }} 39 | 40 | - name: Cargo test 41 | run: cargo test --features "${{ matrix.features }}" --target ${{ matrix.platform.target }} 42 | 43 | - name: Cargo test benches 44 | run: cargo test --benches --features "${{ matrix.features }}" --target ${{ matrix.platform.target }} 45 | 46 | build_result: 47 | name: Result 48 | runs-on: ubuntu-latest 49 | if: always() 50 | needs: 51 | - "test" 52 | 53 | steps: 54 | - name: Success 55 | run: exit 0 56 | if: ${{ !contains(needs.*.result, 'failure') && !contains(needs.*.result, 'cancelled') }} 57 | - name: Failure 58 | run: exit 1 59 | if: contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') 60 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.swp 3 | *.swo 4 | target 5 | Cargo.lock 6 | .idea -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ipc-channel" 3 | version = "0.19.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 | [features] 27 | default = [] 28 | force-inprocess = [] 29 | async = ["dep:futures-core", "dep:futures-channel"] 30 | win32-trace = [] 31 | 32 | [dependencies] 33 | bincode = "1" 34 | crossbeam-channel = "0.5" 35 | fnv = "1.0.3" 36 | futures-channel = { version = "0.3.31", optional = true } 37 | futures-core = { version = "0.3.31", optional = true } 38 | libc = "0.2.162" 39 | serde = { version = "1.0", features = ["rc"] } 40 | uuid = { version = "1", features = ["v4"] } 41 | 42 | [target.'cfg(any(target_os = "linux", target_os = "openbsd", target_os = "freebsd", target_os = "illumos"))'.dependencies] 43 | mio = { version = "1.0", default-features = false, features = ["os-ext"] } 44 | tempfile = "3.4" 45 | 46 | [target.'cfg(target_os = "macos")'.dependencies] 47 | rand = "0.9" 48 | 49 | [dev-dependencies] 50 | crossbeam-utils = "0.8" 51 | futures-test = "0.3" 52 | static_assertions = "1.1.0" 53 | criterion = { version = "0.5", features = ["html_reports"] } 54 | 55 | [target.'cfg(target_os = "windows")'.dependencies.windows] 56 | version = "0.58.0" 57 | features = [ 58 | "Win32_Foundation", 59 | "Win32_System_WindowsProgramming", 60 | "Win32_System_Threading", 61 | "Win32_System_Pipes", 62 | "Win32_System_Memory", 63 | "Win32_System_IO", 64 | "Win32_Storage_FileSystem", 65 | "Win32_Security", 66 | ] 67 | -------------------------------------------------------------------------------- /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 | criterion_main!(benches); 74 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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::Deserialize; 20 | use serde::Serialize; 21 | use std::collections::HashMap; 22 | use std::marker::PhantomData; 23 | use std::pin::Pin; 24 | use std::sync::{LazyLock, Mutex}; 25 | use std::thread; 26 | 27 | /// A stream built from an IPC channel. 28 | pub struct IpcStream(UnboundedReceiver, PhantomData); 29 | 30 | impl Unpin for IpcStream {} 31 | 32 | // A router which routes from an IPC channel to a stream. 33 | struct Router { 34 | // Send `(ipc_recv, send)` to this router to add a route 35 | // from the IPC receiver to the sender. 36 | add_route: UnboundedSender<(OpaqueIpcReceiver, UnboundedSender)>, 37 | 38 | // Wake up the routing thread. 39 | wakeup: Mutex>, 40 | } 41 | 42 | // Lazily initialize a singleton router, 43 | // so we only end up with one routing thread per process. 44 | static ROUTER: LazyLock = LazyLock::new(|| { 45 | let (send, mut recv) = futures_channel::mpsc::unbounded(); 46 | let (waker, wakee) = ipc::channel().expect("Failed to create IPC channel"); 47 | thread::spawn(move || { 48 | let mut receivers = IpcReceiverSet::new().expect("Failed to create receiver set"); 49 | let mut senders = HashMap::>::new(); 50 | let _ = receivers.add(wakee); 51 | while let Ok(mut selections) = receivers.select() { 52 | for selection in selections.drain(..) { 53 | match selection { 54 | IpcSelectionResult::MessageReceived(id, msg) => { 55 | if let Some(sender) = senders.get(&id) { 56 | let _ = sender.unbounded_send(msg); 57 | } 58 | }, 59 | IpcSelectionResult::ChannelClosed(id) => { 60 | senders.remove(&id); 61 | }, 62 | } 63 | } 64 | if !recv.is_terminated() { 65 | while let Ok(Some((receiver, sender))) = recv.try_next() { 66 | if let Ok(id) = receivers.add_opaque(receiver) { 67 | senders.insert(id, sender); 68 | } 69 | } 70 | } 71 | } 72 | }); 73 | Router { 74 | add_route: send, 75 | wakeup: Mutex::new(waker), 76 | } 77 | }); 78 | 79 | impl IpcReceiver 80 | where 81 | T: for<'de> Deserialize<'de> + Serialize, 82 | { 83 | /// Convert this IPC receiver into a stream. 84 | pub fn to_stream(self) -> IpcStream { 85 | let opaque = self.to_opaque(); 86 | let (send, recv) = futures_channel::mpsc::unbounded(); 87 | let _ = ROUTER.add_route.unbounded_send((opaque, send)); 88 | if let Ok(waker) = ROUTER.wakeup.lock() { 89 | let _ = waker.send(()); 90 | } 91 | IpcStream(recv, PhantomData) 92 | } 93 | } 94 | 95 | impl Stream for IpcStream 96 | where 97 | T: for<'de> Deserialize<'de> + Serialize, 98 | { 99 | type Item = Result; 100 | 101 | fn poll_next(mut self: Pin<&mut Self>, ctx: &mut Context) -> Poll> { 102 | let recv = Pin::new(&mut self.0); 103 | match recv.poll_next(ctx) { 104 | Poll::Ready(Some(msg)) => Poll::Ready(Some(msg.to())), 105 | Poll::Ready(None) => Poll::Ready(None), 106 | Poll::Pending => Poll::Pending, 107 | } 108 | } 109 | } 110 | 111 | impl FusedStream for IpcStream 112 | where 113 | T: for<'de> Deserialize<'de> + Serialize, 114 | { 115 | fn is_terminated(&self) -> bool { 116 | self.0.is_terminated() 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /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::{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 | thread_local! { 32 | static OS_IPC_SHARED_MEMORY_REGIONS_FOR_DESERIALIZATION: 33 | RefCell>> = const { RefCell::new(Vec::new()) } 34 | } 35 | thread_local! { 36 | static OS_IPC_CHANNELS_FOR_SERIALIZATION: RefCell> = const { RefCell::new(Vec::new()) } 37 | } 38 | thread_local! { 39 | static OS_IPC_SHARED_MEMORY_REGIONS_FOR_SERIALIZATION: RefCell> = 40 | const { RefCell::new(Vec::new()) } 41 | } 42 | 43 | #[derive(Debug)] 44 | pub enum IpcError { 45 | Bincode(bincode::Error), 46 | Io(io::Error), 47 | Disconnected, 48 | } 49 | 50 | impl fmt::Display for IpcError { 51 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 52 | match *self { 53 | IpcError::Bincode(ref err) => write!(fmt, "bincode error: {}", err), 54 | IpcError::Io(ref err) => write!(fmt, "io error: {}", err), 55 | IpcError::Disconnected => write!(fmt, "disconnected"), 56 | } 57 | } 58 | } 59 | 60 | impl StdError for IpcError { 61 | fn source(&self) -> Option<&(dyn StdError + 'static)> { 62 | match *self { 63 | IpcError::Bincode(ref err) => Some(err), 64 | IpcError::Io(ref err) => Some(err), 65 | IpcError::Disconnected => None, 66 | } 67 | } 68 | } 69 | 70 | #[derive(Debug)] 71 | pub enum TryRecvError { 72 | IpcError(IpcError), 73 | Empty, 74 | } 75 | 76 | impl fmt::Display for TryRecvError { 77 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 78 | match *self { 79 | TryRecvError::IpcError(ref err) => write!(fmt, "ipc error: {}", err), 80 | TryRecvError::Empty => write!(fmt, "empty"), 81 | } 82 | } 83 | } 84 | 85 | impl StdError for TryRecvError { 86 | fn source(&self) -> Option<&(dyn StdError + 'static)> { 87 | match *self { 88 | TryRecvError::IpcError(ref err) => Some(err), 89 | TryRecvError::Empty => None, 90 | } 91 | } 92 | } 93 | 94 | /// Create a connected [IpcSender] and [IpcReceiver] that 95 | /// transfer messages of a given type provided by type `T` 96 | /// or inferred by the types of messages sent by the sender. 97 | /// 98 | /// Messages sent by the sender will be available to the 99 | /// receiver even if the sender or receiver has been moved 100 | /// to a different process. In addition, receivers and senders 101 | /// may be sent over an existing channel. 102 | /// 103 | /// # Examples 104 | /// 105 | /// ``` 106 | /// # use ipc_channel::ipc; 107 | /// 108 | /// let payload = "Hello, World!".to_owned(); 109 | /// 110 | /// // Create a channel 111 | /// let (tx, rx) = ipc::channel().unwrap(); 112 | /// 113 | /// // Send data 114 | /// tx.send(payload).unwrap(); 115 | /// 116 | /// // Receive the data 117 | /// let response = rx.recv().unwrap(); 118 | /// 119 | /// assert_eq!(response, "Hello, World!".to_owned()); 120 | /// ``` 121 | /// 122 | /// [IpcSender]: struct.IpcSender.html 123 | /// [IpcReceiver]: struct.IpcReceiver.html 124 | pub fn channel() -> Result<(IpcSender, IpcReceiver), io::Error> 125 | where 126 | T: for<'de> Deserialize<'de> + Serialize, 127 | { 128 | let (os_sender, os_receiver) = platform::channel()?; 129 | let ipc_receiver = IpcReceiver { 130 | os_receiver, 131 | phantom: PhantomData, 132 | }; 133 | let ipc_sender = IpcSender { 134 | os_sender, 135 | phantom: PhantomData, 136 | }; 137 | Ok((ipc_sender, ipc_receiver)) 138 | } 139 | 140 | /// Create a connected [IpcBytesSender] and [IpcBytesReceiver]. 141 | /// 142 | /// Note: The [IpcBytesSender] transfers messages of the type `[u8]` 143 | /// and the [IpcBytesReceiver] receives a `Vec`. This sender/receiver 144 | /// type does not serialize/deserialize messages through `serde`, making 145 | /// it more efficient where applicable. 146 | /// 147 | /// # Examples 148 | /// 149 | /// ``` 150 | /// # use ipc_channel::ipc; 151 | /// 152 | /// let payload = b"'Tis but a scratch!!"; 153 | /// 154 | /// // Create a channel 155 | /// let (tx, rx) = ipc::bytes_channel().unwrap(); 156 | /// 157 | /// // Send data 158 | /// tx.send(payload).unwrap(); 159 | /// 160 | /// // Receive the data 161 | /// let response = rx.recv().unwrap(); 162 | /// 163 | /// assert_eq!(response, payload); 164 | /// ``` 165 | /// 166 | /// [IpcBytesReceiver]: struct.IpcBytesReceiver.html 167 | /// [IpcBytesSender]: struct.IpcBytesSender.html 168 | pub fn bytes_channel() -> Result<(IpcBytesSender, IpcBytesReceiver), io::Error> { 169 | let (os_sender, os_receiver) = platform::channel()?; 170 | let ipc_bytes_receiver = IpcBytesReceiver { os_receiver }; 171 | let ipc_bytes_sender = IpcBytesSender { os_sender }; 172 | Ok((ipc_bytes_sender, ipc_bytes_receiver)) 173 | } 174 | 175 | /// Receiving end of a channel using serialized messages. 176 | /// 177 | /// # Examples 178 | /// 179 | /// ## Blocking IO 180 | /// 181 | /// ``` 182 | /// # use ipc_channel::ipc; 183 | /// # 184 | /// # let (tx, rx) = ipc::channel().unwrap(); 185 | /// # 186 | /// # let q = "Answer to the ultimate question of life, the universe, and everything"; 187 | /// # 188 | /// # tx.send(q.to_owned()).unwrap(); 189 | /// let response = rx.recv().unwrap(); 190 | /// println!("Received data..."); 191 | /// # assert_eq!(response, q); 192 | /// ``` 193 | /// 194 | /// ## Non-blocking IO 195 | /// 196 | /// ``` 197 | /// # use ipc_channel::ipc; 198 | /// # 199 | /// # let (tx, rx) = ipc::channel().unwrap(); 200 | /// # 201 | /// # let answer = "42"; 202 | /// # 203 | /// # tx.send(answer.to_owned()).unwrap(); 204 | /// loop { 205 | /// match rx.try_recv() { 206 | /// Ok(res) => { 207 | /// // Do something interesting with your result 208 | /// println!("Received data..."); 209 | /// break; 210 | /// }, 211 | /// Err(_) => { 212 | /// // Do something else useful while we wait 213 | /// println!("Still waiting..."); 214 | /// } 215 | /// } 216 | /// } 217 | /// ``` 218 | /// 219 | /// ## Embedding Receivers 220 | /// 221 | /// ``` 222 | /// # use ipc_channel::ipc; 223 | /// # 224 | /// let (tx, rx) = ipc::channel().unwrap(); 225 | /// let (embedded_tx, embedded_rx) = ipc::channel().unwrap(); 226 | /// # let data = [0x45, 0x6d, 0x62, 0x65, 0x64, 0x64, 0x65, 0x64, 0x00]; 227 | /// // Send the IpcReceiver 228 | /// tx.send(embedded_rx).unwrap(); 229 | /// # embedded_tx.send(data.to_owned()).unwrap(); 230 | /// // Receive the sent IpcReceiver 231 | /// let received_rx = rx.recv().unwrap(); 232 | /// // Receive any data sent to the received IpcReceiver 233 | /// let rx_data = received_rx.recv().unwrap(); 234 | /// # assert_eq!(rx_data, data); 235 | /// ``` 236 | /// 237 | /// # Implementation details 238 | /// 239 | /// Each [IpcReceiver] is backed by the OS specific implementations of `OsIpcReceiver`. 240 | /// 241 | /// [IpcReceiver]: struct.IpcReceiver.html 242 | #[derive(Debug)] 243 | pub struct IpcReceiver { 244 | os_receiver: OsIpcReceiver, 245 | phantom: PhantomData, 246 | } 247 | 248 | impl IpcReceiver 249 | where 250 | T: for<'de> Deserialize<'de> + Serialize, 251 | { 252 | /// Blocking receive. 253 | pub fn recv(&self) -> Result { 254 | self.os_receiver.recv()?.to().map_err(IpcError::Bincode) 255 | } 256 | 257 | /// Non-blocking receive 258 | pub fn try_recv(&self) -> Result { 259 | self.os_receiver 260 | .try_recv()? 261 | .to() 262 | .map_err(IpcError::Bincode) 263 | .map_err(TryRecvError::IpcError) 264 | } 265 | 266 | /// Blocks for up to the specified duration attempting to receive a message. 267 | /// 268 | /// This may block for longer than the specified duration if the channel is busy. If your timeout 269 | /// exceeds the duration that your operating system can represent in milliseconds, this may 270 | /// block forever. At the time of writing, the smallest duration that may trigger this behavior 271 | /// is over 24 days. 272 | pub fn try_recv_timeout(&self, duration: Duration) -> Result { 273 | self.os_receiver 274 | .try_recv_timeout(duration)? 275 | .to() 276 | .map_err(IpcError::Bincode) 277 | .map_err(TryRecvError::IpcError) 278 | } 279 | 280 | /// Erase the type of the channel. 281 | /// 282 | /// Useful for adding routes to a `RouterProxy`. 283 | pub fn to_opaque(self) -> OpaqueIpcReceiver { 284 | OpaqueIpcReceiver { 285 | os_receiver: self.os_receiver, 286 | } 287 | } 288 | } 289 | 290 | impl<'de, T> Deserialize<'de> for IpcReceiver { 291 | fn deserialize(deserializer: D) -> Result 292 | where 293 | D: Deserializer<'de>, 294 | { 295 | let os_receiver = deserialize_os_ipc_receiver(deserializer)?; 296 | Ok(IpcReceiver { 297 | os_receiver, 298 | phantom: PhantomData, 299 | }) 300 | } 301 | } 302 | 303 | impl Serialize for IpcReceiver { 304 | fn serialize(&self, serializer: S) -> Result 305 | where 306 | S: Serializer, 307 | { 308 | serialize_os_ipc_receiver(&self.os_receiver, serializer) 309 | } 310 | } 311 | 312 | /// Sending end of a channel using serialized messages. 313 | /// 314 | /// 315 | /// ## Embedding Senders 316 | /// 317 | /// ``` 318 | /// # use ipc_channel::ipc; 319 | /// # 320 | /// # let (tx, rx) = ipc::channel().unwrap(); 321 | /// # let (embedded_tx, embedded_rx) = ipc::channel().unwrap(); 322 | /// # let data = [0x45, 0x6d, 0x62, 0x65, 0x64, 0x64, 0x65, 0x64, 0x00]; 323 | /// // Send the IpcSender 324 | /// tx.send(embedded_tx).unwrap(); 325 | /// // Receive the sent IpcSender 326 | /// let received_tx = rx.recv().unwrap(); 327 | /// // Send data from the received IpcSender 328 | /// received_tx.send(data.clone()).unwrap(); 329 | /// # let rx_data = embedded_rx.recv().unwrap(); 330 | /// # assert_eq!(rx_data, data); 331 | /// ``` 332 | #[derive(Debug)] 333 | pub struct IpcSender { 334 | os_sender: OsIpcSender, 335 | phantom: PhantomData, 336 | } 337 | 338 | impl Clone for IpcSender 339 | where 340 | T: Serialize, 341 | { 342 | fn clone(&self) -> IpcSender { 343 | IpcSender { 344 | os_sender: self.os_sender.clone(), 345 | phantom: PhantomData, 346 | } 347 | } 348 | } 349 | 350 | impl IpcSender 351 | where 352 | T: Serialize, 353 | { 354 | /// Create an [IpcSender] connected to a previously defined [IpcOneShotServer]. 355 | /// 356 | /// This function should not be called more than once per [IpcOneShotServer], 357 | /// otherwise the behaviour is unpredictable. 358 | /// See [issue 378](https://github.com/servo/ipc-channel/issues/378) for details. 359 | /// 360 | /// [IpcSender]: struct.IpcSender.html 361 | /// [IpcOneShotServer]: struct.IpcOneShotServer.html 362 | pub fn connect(name: String) -> Result, io::Error> { 363 | Ok(IpcSender { 364 | os_sender: OsIpcSender::connect(name)?, 365 | phantom: PhantomData, 366 | }) 367 | } 368 | 369 | /// Send data across the channel to the receiver. 370 | pub fn send(&self, data: T) -> Result<(), bincode::Error> { 371 | let mut bytes = Vec::with_capacity(4096); 372 | OS_IPC_CHANNELS_FOR_SERIALIZATION.with(|os_ipc_channels_for_serialization| { 373 | OS_IPC_SHARED_MEMORY_REGIONS_FOR_SERIALIZATION.with( 374 | |os_ipc_shared_memory_regions_for_serialization| { 375 | let old_os_ipc_channels = 376 | mem::take(&mut *os_ipc_channels_for_serialization.borrow_mut()); 377 | let old_os_ipc_shared_memory_regions = mem::take( 378 | &mut *os_ipc_shared_memory_regions_for_serialization.borrow_mut(), 379 | ); 380 | let os_ipc_shared_memory_regions; 381 | let os_ipc_channels; 382 | { 383 | bincode::serialize_into(&mut bytes, &data)?; 384 | os_ipc_channels = mem::replace( 385 | &mut *os_ipc_channels_for_serialization.borrow_mut(), 386 | old_os_ipc_channels, 387 | ); 388 | os_ipc_shared_memory_regions = mem::replace( 389 | &mut *os_ipc_shared_memory_regions_for_serialization.borrow_mut(), 390 | old_os_ipc_shared_memory_regions, 391 | ); 392 | }; 393 | Ok(self.os_sender.send( 394 | &bytes[..], 395 | os_ipc_channels, 396 | os_ipc_shared_memory_regions, 397 | )?) 398 | }, 399 | ) 400 | }) 401 | } 402 | 403 | pub fn to_opaque(self) -> OpaqueIpcSender { 404 | OpaqueIpcSender { 405 | os_sender: self.os_sender, 406 | } 407 | } 408 | } 409 | 410 | impl<'de, T> Deserialize<'de> for IpcSender { 411 | fn deserialize(deserializer: D) -> Result 412 | where 413 | D: Deserializer<'de>, 414 | { 415 | let os_sender = deserialize_os_ipc_sender(deserializer)?; 416 | Ok(IpcSender { 417 | os_sender, 418 | phantom: PhantomData, 419 | }) 420 | } 421 | } 422 | 423 | impl Serialize for IpcSender { 424 | fn serialize(&self, serializer: S) -> Result 425 | where 426 | S: Serializer, 427 | { 428 | serialize_os_ipc_sender(&self.os_sender, serializer) 429 | } 430 | } 431 | 432 | /// Collection of [IpcReceiver]s moved into the set; thus creating a common 433 | /// (and exclusive) endpoint for receiving messages on any of the added 434 | /// channels. 435 | /// 436 | /// # Examples 437 | /// 438 | /// ``` 439 | /// # use ipc_channel::ipc::{self, IpcReceiverSet, IpcSelectionResult}; 440 | /// let data = vec![0x52, 0x75, 0x73, 0x74, 0x00]; 441 | /// let (tx, rx) = ipc::channel().unwrap(); 442 | /// let mut rx_set = IpcReceiverSet::new().unwrap(); 443 | /// 444 | /// // Add the receiver to the receiver set and send the data 445 | /// // from the sender 446 | /// let rx_id = rx_set.add(rx).unwrap(); 447 | /// tx.send(data.clone()).unwrap(); 448 | /// 449 | /// // Poll the receiver set for any readable events 450 | /// for event in rx_set.select().unwrap() { 451 | /// match event { 452 | /// IpcSelectionResult::MessageReceived(id, message) => { 453 | /// let rx_data: Vec = message.to().unwrap(); 454 | /// assert_eq!(id, rx_id); 455 | /// assert_eq!(data, rx_data); 456 | /// println!("Received: {:?} from {}...", data, id); 457 | /// }, 458 | /// IpcSelectionResult::ChannelClosed(id) => { 459 | /// assert_eq!(id, rx_id); 460 | /// println!("No more data from {}...", id); 461 | /// } 462 | /// } 463 | /// } 464 | /// ``` 465 | /// [IpcReceiver]: struct.IpcReceiver.html 466 | pub struct IpcReceiverSet { 467 | os_receiver_set: OsIpcReceiverSet, 468 | } 469 | 470 | impl IpcReceiverSet { 471 | /// Create a new empty [IpcReceiverSet]. 472 | /// 473 | /// Receivers may then be added to the set with the [add] 474 | /// method. 475 | /// 476 | /// [add]: #method.add 477 | /// [IpcReceiverSet]: struct.IpcReceiverSet.html 478 | pub fn new() -> Result { 479 | Ok(IpcReceiverSet { 480 | os_receiver_set: OsIpcReceiverSet::new()?, 481 | }) 482 | } 483 | 484 | /// Add and consume the [IpcReceiver] to the set of receivers to be polled. 485 | /// [IpcReceiver]: struct.IpcReceiver.html 486 | pub fn add(&mut self, receiver: IpcReceiver) -> Result 487 | where 488 | T: for<'de> Deserialize<'de> + Serialize, 489 | { 490 | Ok(self.os_receiver_set.add(receiver.os_receiver)?) 491 | } 492 | 493 | /// Add an [OpaqueIpcReceiver] to the set of receivers to be polled. 494 | /// [OpaqueIpcReceiver]: struct.OpaqueIpcReceiver.html 495 | pub fn add_opaque(&mut self, receiver: OpaqueIpcReceiver) -> Result { 496 | Ok(self.os_receiver_set.add(receiver.os_receiver)?) 497 | } 498 | 499 | /// Wait for IPC messages received on any of the receivers in the set. The 500 | /// method will return multiple events. An event may be either a message 501 | /// received or a channel closed event. 502 | /// 503 | /// [IpcReceiver]: struct.IpcReceiver.html 504 | pub fn select(&mut self) -> Result, io::Error> { 505 | let results = self.os_receiver_set.select()?; 506 | Ok(results 507 | .into_iter() 508 | .map(|result| match result { 509 | OsIpcSelectionResult::DataReceived(os_receiver_id, ipc_message) => { 510 | IpcSelectionResult::MessageReceived(os_receiver_id, ipc_message) 511 | }, 512 | OsIpcSelectionResult::ChannelClosed(os_receiver_id) => { 513 | IpcSelectionResult::ChannelClosed(os_receiver_id) 514 | }, 515 | }) 516 | .collect()) 517 | } 518 | } 519 | 520 | /// Shared memory descriptor that will be made accessible to the receiver 521 | /// of an IPC message that contains the discriptor. 522 | /// 523 | /// # Examples 524 | /// ``` 525 | /// # use ipc_channel::ipc::{self, IpcSharedMemory}; 526 | /// # let (tx, rx) = ipc::channel().unwrap(); 527 | /// # let data = [0x76, 0x69, 0x6d, 0x00]; 528 | /// let shmem = IpcSharedMemory::from_bytes(&data); 529 | /// tx.send(shmem.clone()).unwrap(); 530 | /// # let rx_shmem = rx.recv().unwrap(); 531 | /// # assert_eq!(shmem, rx_shmem); 532 | /// ``` 533 | #[derive(Clone, Debug, PartialEq)] 534 | pub struct IpcSharedMemory { 535 | /// None represents no data (empty slice) 536 | os_shared_memory: Option, 537 | } 538 | 539 | impl Deref for IpcSharedMemory { 540 | type Target = [u8]; 541 | 542 | #[inline] 543 | fn deref(&self) -> &[u8] { 544 | if let Some(os_shared_memory) = &self.os_shared_memory { 545 | os_shared_memory 546 | } else { 547 | &[] 548 | } 549 | } 550 | } 551 | 552 | impl IpcSharedMemory { 553 | /// Returns a mutable reference to the deref of this [`IpcSharedMemory`]. 554 | /// 555 | /// # Safety 556 | /// 557 | /// This is safe if there is only one reader/writer on the data. 558 | /// User can achieve this by not cloning [`IpcSharedMemory`] 559 | /// and serializing/deserializing only once. 560 | #[inline] 561 | pub unsafe fn deref_mut(&mut self) -> &mut [u8] { 562 | if let Some(os_shared_memory) = &mut self.os_shared_memory { 563 | os_shared_memory.deref_mut() 564 | } else { 565 | &mut [] 566 | } 567 | } 568 | } 569 | 570 | impl<'de> Deserialize<'de> for IpcSharedMemory { 571 | fn deserialize(deserializer: D) -> Result 572 | where 573 | D: Deserializer<'de>, 574 | { 575 | let index: usize = Deserialize::deserialize(deserializer)?; 576 | if index == usize::MAX { 577 | return Ok(IpcSharedMemory::empty()); 578 | } 579 | 580 | let os_shared_memory = OS_IPC_SHARED_MEMORY_REGIONS_FOR_DESERIALIZATION.with( 581 | |os_ipc_shared_memory_regions_for_deserialization| { 582 | let mut regions = os_ipc_shared_memory_regions_for_deserialization.borrow_mut(); 583 | let Some(region) = regions.get_mut(index) else { 584 | return Err(format!("Cannot consume shared memory region at index {index}, there are only {} regions available", regions.len())); 585 | }; 586 | 587 | region.take().ok_or_else(|| format!("Shared memory region {index} has already been consumed")) 588 | }, 589 | ).map_err(D::Error::custom)?; 590 | 591 | Ok(IpcSharedMemory { 592 | os_shared_memory: Some(os_shared_memory), 593 | }) 594 | } 595 | } 596 | 597 | impl Serialize for IpcSharedMemory { 598 | fn serialize(&self, serializer: S) -> Result 599 | where 600 | S: Serializer, 601 | { 602 | if let Some(os_shared_memory) = &self.os_shared_memory { 603 | let index = OS_IPC_SHARED_MEMORY_REGIONS_FOR_SERIALIZATION.with( 604 | |os_ipc_shared_memory_regions_for_serialization| { 605 | let mut os_ipc_shared_memory_regions_for_serialization = 606 | os_ipc_shared_memory_regions_for_serialization.borrow_mut(); 607 | let index = os_ipc_shared_memory_regions_for_serialization.len(); 608 | os_ipc_shared_memory_regions_for_serialization.push(os_shared_memory.clone()); 609 | index 610 | }, 611 | ); 612 | debug_assert!(index < usize::MAX); 613 | index 614 | } else { 615 | usize::MAX 616 | } 617 | .serialize(serializer) 618 | } 619 | } 620 | 621 | impl IpcSharedMemory { 622 | const fn empty() -> Self { 623 | Self { 624 | os_shared_memory: None, 625 | } 626 | } 627 | 628 | /// Create shared memory initialized with the bytes provided. 629 | pub fn from_bytes(bytes: &[u8]) -> IpcSharedMemory { 630 | if bytes.is_empty() { 631 | IpcSharedMemory::empty() 632 | } else { 633 | IpcSharedMemory { 634 | os_shared_memory: Some(OsIpcSharedMemory::from_bytes(bytes)), 635 | } 636 | } 637 | } 638 | 639 | /// Create a chunk of shared memory that is filled with the byte 640 | /// provided. 641 | pub fn from_byte(byte: u8, length: usize) -> IpcSharedMemory { 642 | if length == 0 { 643 | IpcSharedMemory::empty() 644 | } else { 645 | IpcSharedMemory { 646 | os_shared_memory: Some(OsIpcSharedMemory::from_byte(byte, length)), 647 | } 648 | } 649 | } 650 | } 651 | 652 | /// Result for readable events returned from [IpcReceiverSet::select]. 653 | /// 654 | /// [IpcReceiverSet::select]: struct.IpcReceiverSet.html#method.select 655 | pub enum IpcSelectionResult { 656 | /// A message received from the [`IpcReceiver`] in the [`IpcMessage`] form, 657 | /// identified by the `u64` value. 658 | MessageReceived(u64, IpcMessage), 659 | /// The channel has been closed for the [IpcReceiver] identified by the `u64` value. 660 | /// [IpcReceiver]: struct.IpcReceiver.html 661 | ChannelClosed(u64), 662 | } 663 | 664 | impl IpcSelectionResult { 665 | /// Helper method to move the value out of the [IpcSelectionResult] if it 666 | /// is [MessageReceived]. 667 | /// 668 | /// # Panics 669 | /// 670 | /// If the result is [ChannelClosed] this call will panic. 671 | /// 672 | /// [IpcSelectionResult]: enum.IpcSelectionResult.html 673 | /// [MessageReceived]: enum.IpcSelectionResult.html#variant.MessageReceived 674 | /// [ChannelClosed]: enum.IpcSelectionResult.html#variant.ChannelClosed 675 | pub fn unwrap(self) -> (u64, IpcMessage) { 676 | match self { 677 | IpcSelectionResult::MessageReceived(id, message) => (id, message), 678 | IpcSelectionResult::ChannelClosed(id) => { 679 | panic!("IpcSelectionResult::unwrap(): channel {} closed", id) 680 | }, 681 | } 682 | } 683 | } 684 | 685 | /// Structure used to represent a raw message from an [`IpcSender`]. 686 | /// 687 | /// Use the [to] method to deserialize the raw result into the requested type. 688 | /// 689 | /// [to]: #method.to 690 | #[derive(PartialEq)] 691 | pub struct IpcMessage { 692 | pub(crate) data: Vec, 693 | pub(crate) os_ipc_channels: Vec, 694 | pub(crate) os_ipc_shared_memory_regions: Vec, 695 | } 696 | 697 | impl IpcMessage { 698 | /// Create a new [`IpcMessage`] with data and without any [`OsOpaqueIpcChannel`]s and 699 | /// [`OsIpcSharedMemory`] regions. 700 | pub fn from_data(data: Vec) -> Self { 701 | Self { 702 | data, 703 | os_ipc_channels: vec![], 704 | os_ipc_shared_memory_regions: vec![], 705 | } 706 | } 707 | } 708 | 709 | impl Debug for IpcMessage { 710 | fn fmt(&self, formatter: &mut Formatter) -> Result<(), fmt::Error> { 711 | match String::from_utf8(self.data.clone()) { 712 | Ok(string) => string.chars().take(256).collect::().fmt(formatter), 713 | Err(..) => self.data[0..min(self.data.len(), 256)].fmt(formatter), 714 | } 715 | } 716 | } 717 | 718 | impl IpcMessage { 719 | pub(crate) fn new( 720 | data: Vec, 721 | os_ipc_channels: Vec, 722 | os_ipc_shared_memory_regions: Vec, 723 | ) -> IpcMessage { 724 | IpcMessage { 725 | data, 726 | os_ipc_channels, 727 | os_ipc_shared_memory_regions, 728 | } 729 | } 730 | 731 | /// Deserialize the raw data in the contained message into the inferred type. 732 | pub fn to(mut self) -> Result 733 | where 734 | T: for<'de> Deserialize<'de> + Serialize, 735 | { 736 | OS_IPC_CHANNELS_FOR_DESERIALIZATION.with(|os_ipc_channels_for_deserialization| { 737 | OS_IPC_SHARED_MEMORY_REGIONS_FOR_DESERIALIZATION.with( 738 | |os_ipc_shared_memory_regions_for_deserialization| { 739 | mem::swap( 740 | &mut *os_ipc_channels_for_deserialization.borrow_mut(), 741 | &mut self.os_ipc_channels, 742 | ); 743 | let old_ipc_shared_memory_regions_for_deserialization = mem::replace( 744 | &mut *os_ipc_shared_memory_regions_for_deserialization.borrow_mut(), 745 | self.os_ipc_shared_memory_regions 746 | .into_iter() 747 | .map(Some) 748 | .collect(), 749 | ); 750 | let result = bincode::deserialize(&self.data[..]); 751 | *os_ipc_shared_memory_regions_for_deserialization.borrow_mut() = 752 | old_ipc_shared_memory_regions_for_deserialization; 753 | mem::swap( 754 | &mut *os_ipc_channels_for_deserialization.borrow_mut(), 755 | &mut self.os_ipc_channels, 756 | ); 757 | /* Error check comes after doing cleanup, 758 | * since we need the cleanup both in the success and the error cases. */ 759 | result 760 | }, 761 | ) 762 | }) 763 | } 764 | } 765 | 766 | #[derive(Clone, Debug)] 767 | pub struct OpaqueIpcSender { 768 | os_sender: OsIpcSender, 769 | } 770 | 771 | impl OpaqueIpcSender { 772 | pub fn to<'de, T>(self) -> IpcSender 773 | where 774 | T: Deserialize<'de> + Serialize, 775 | { 776 | IpcSender { 777 | os_sender: self.os_sender, 778 | phantom: PhantomData, 779 | } 780 | } 781 | } 782 | 783 | impl<'de> Deserialize<'de> for OpaqueIpcSender { 784 | fn deserialize(deserializer: D) -> Result 785 | where 786 | D: Deserializer<'de>, 787 | { 788 | let os_sender = deserialize_os_ipc_sender(deserializer)?; 789 | Ok(OpaqueIpcSender { os_sender }) 790 | } 791 | } 792 | 793 | impl Serialize for OpaqueIpcSender { 794 | fn serialize(&self, serializer: S) -> Result 795 | where 796 | S: Serializer, 797 | { 798 | serialize_os_ipc_sender(&self.os_sender, serializer) 799 | } 800 | } 801 | 802 | #[derive(Debug)] 803 | pub struct OpaqueIpcReceiver { 804 | os_receiver: OsIpcReceiver, 805 | } 806 | 807 | impl OpaqueIpcReceiver { 808 | pub fn to<'de, T>(self) -> IpcReceiver 809 | where 810 | T: Deserialize<'de> + Serialize, 811 | { 812 | IpcReceiver { 813 | os_receiver: self.os_receiver, 814 | phantom: PhantomData, 815 | } 816 | } 817 | } 818 | 819 | impl<'de> Deserialize<'de> for OpaqueIpcReceiver { 820 | fn deserialize(deserializer: D) -> Result 821 | where 822 | D: Deserializer<'de>, 823 | { 824 | let os_receiver = deserialize_os_ipc_receiver(deserializer)?; 825 | Ok(OpaqueIpcReceiver { os_receiver }) 826 | } 827 | } 828 | 829 | impl Serialize for OpaqueIpcReceiver { 830 | fn serialize(&self, serializer: S) -> Result 831 | where 832 | S: Serializer, 833 | { 834 | serialize_os_ipc_receiver(&self.os_receiver, serializer) 835 | } 836 | } 837 | 838 | /// A server associated with a given name. The server is "one-shot" because 839 | /// it accepts only one connect request from a client. 840 | /// 841 | /// # Examples 842 | /// 843 | /// ## Basic Usage 844 | /// 845 | /// ``` 846 | /// use ipc_channel::ipc::{self, IpcOneShotServer, IpcSender, IpcReceiver}; 847 | /// 848 | /// let (server, server_name) = IpcOneShotServer::new().unwrap(); 849 | /// let tx: IpcSender> = IpcSender::connect(server_name).unwrap(); 850 | /// 851 | /// tx.send(vec![0x10, 0x11, 0x12, 0x13]).unwrap(); 852 | /// let (_, data): (_, Vec) = server.accept().unwrap(); 853 | /// assert_eq!(data, vec![0x10, 0x11, 0x12, 0x13]); 854 | /// ``` 855 | /// 856 | /// ## Sending an [IpcSender] 857 | /// ``` 858 | /// use ipc_channel::ipc::{self, IpcOneShotServer, IpcSender, IpcReceiver}; 859 | /// let (server, name) = IpcOneShotServer::new().unwrap(); 860 | /// 861 | /// let (tx1, rx1): (IpcSender>, IpcReceiver>) = ipc::channel().unwrap(); 862 | /// let tx0 = IpcSender::connect(name).unwrap(); 863 | /// tx0.send(tx1).unwrap(); 864 | /// 865 | /// let (_, tx1): (_, IpcSender>) = server.accept().unwrap(); 866 | /// tx1.send(vec![0x48, 0x65, 0x6b, 0x6b, 0x6f, 0x00]).unwrap(); 867 | /// 868 | /// let data = rx1.recv().unwrap(); 869 | /// assert_eq!(data, vec![0x48, 0x65, 0x6b, 0x6b, 0x6f, 0x00]); 870 | /// ``` 871 | /// [IpcSender]: struct.IpcSender.html 872 | pub struct IpcOneShotServer { 873 | os_server: OsIpcOneShotServer, 874 | phantom: PhantomData, 875 | } 876 | 877 | impl IpcOneShotServer 878 | where 879 | T: for<'de> Deserialize<'de> + Serialize, 880 | { 881 | pub fn new() -> Result<(IpcOneShotServer, String), io::Error> { 882 | let (os_server, name) = OsIpcOneShotServer::new()?; 883 | Ok(( 884 | IpcOneShotServer { 885 | os_server, 886 | phantom: PhantomData, 887 | }, 888 | name, 889 | )) 890 | } 891 | 892 | pub fn accept(self) -> Result<(IpcReceiver, T), bincode::Error> { 893 | let (os_receiver, ipc_message) = self.os_server.accept()?; 894 | Ok(( 895 | IpcReceiver { 896 | os_receiver, 897 | phantom: PhantomData, 898 | }, 899 | ipc_message.to()?, 900 | )) 901 | } 902 | } 903 | 904 | /// Receiving end of a channel that does not used serialized messages. 905 | #[derive(Debug)] 906 | pub struct IpcBytesReceiver { 907 | os_receiver: OsIpcReceiver, 908 | } 909 | 910 | impl IpcBytesReceiver { 911 | /// Blocking receive. 912 | #[inline] 913 | pub fn recv(&self) -> Result, IpcError> { 914 | match self.os_receiver.recv() { 915 | Ok(ipc_message) => Ok(ipc_message.data), 916 | Err(err) => Err(err.into()), 917 | } 918 | } 919 | 920 | /// Non-blocking receive 921 | pub fn try_recv(&self) -> Result, TryRecvError> { 922 | match self.os_receiver.try_recv() { 923 | Ok(ipc_message) => Ok(ipc_message.data), 924 | Err(err) => Err(err.into()), 925 | } 926 | } 927 | } 928 | 929 | impl<'de> Deserialize<'de> for IpcBytesReceiver { 930 | fn deserialize(deserializer: D) -> Result 931 | where 932 | D: Deserializer<'de>, 933 | { 934 | let os_receiver = deserialize_os_ipc_receiver(deserializer)?; 935 | Ok(IpcBytesReceiver { os_receiver }) 936 | } 937 | } 938 | 939 | impl Serialize for IpcBytesReceiver { 940 | fn serialize(&self, serializer: S) -> Result 941 | where 942 | S: Serializer, 943 | { 944 | serialize_os_ipc_receiver(&self.os_receiver, serializer) 945 | } 946 | } 947 | 948 | /// Sending end of a channel that does not used serialized messages. 949 | #[derive(Debug)] 950 | pub struct IpcBytesSender { 951 | os_sender: OsIpcSender, 952 | } 953 | 954 | impl Clone for IpcBytesSender { 955 | fn clone(&self) -> IpcBytesSender { 956 | IpcBytesSender { 957 | os_sender: self.os_sender.clone(), 958 | } 959 | } 960 | } 961 | 962 | impl<'de> Deserialize<'de> for IpcBytesSender { 963 | fn deserialize(deserializer: D) -> Result 964 | where 965 | D: Deserializer<'de>, 966 | { 967 | let os_sender = deserialize_os_ipc_sender(deserializer)?; 968 | Ok(IpcBytesSender { os_sender }) 969 | } 970 | } 971 | 972 | impl Serialize for IpcBytesSender { 973 | fn serialize(&self, serializer: S) -> Result 974 | where 975 | S: Serializer, 976 | { 977 | serialize_os_ipc_sender(&self.os_sender, serializer) 978 | } 979 | } 980 | 981 | impl IpcBytesSender { 982 | #[inline] 983 | pub fn send(&self, data: &[u8]) -> Result<(), io::Error> { 984 | self.os_sender 985 | .send(data, vec![], vec![]) 986 | .map_err(io::Error::from) 987 | } 988 | } 989 | 990 | fn serialize_os_ipc_sender(os_ipc_sender: &OsIpcSender, serializer: S) -> Result 991 | where 992 | S: Serializer, 993 | { 994 | let index = OS_IPC_CHANNELS_FOR_SERIALIZATION.with(|os_ipc_channels_for_serialization| { 995 | let mut os_ipc_channels_for_serialization = os_ipc_channels_for_serialization.borrow_mut(); 996 | let index = os_ipc_channels_for_serialization.len(); 997 | os_ipc_channels_for_serialization.push(OsIpcChannel::Sender(os_ipc_sender.clone())); 998 | index 999 | }); 1000 | index.serialize(serializer) 1001 | } 1002 | 1003 | fn deserialize_os_ipc_sender<'de, D>(deserializer: D) -> Result 1004 | where 1005 | D: Deserializer<'de>, 1006 | { 1007 | let index: usize = Deserialize::deserialize(deserializer)?; 1008 | OS_IPC_CHANNELS_FOR_DESERIALIZATION.with(|os_ipc_channels_for_deserialization| { 1009 | // FIXME(pcwalton): This could panic if the data was corrupt and the index was out of 1010 | // bounds. We should return an `Err` result instead. 1011 | Ok(os_ipc_channels_for_deserialization.borrow_mut()[index].to_sender()) 1012 | }) 1013 | } 1014 | 1015 | fn serialize_os_ipc_receiver( 1016 | os_receiver: &OsIpcReceiver, 1017 | serializer: S, 1018 | ) -> Result 1019 | where 1020 | S: Serializer, 1021 | { 1022 | let index = OS_IPC_CHANNELS_FOR_SERIALIZATION.with(|os_ipc_channels_for_serialization| { 1023 | let mut os_ipc_channels_for_serialization = os_ipc_channels_for_serialization.borrow_mut(); 1024 | let index = os_ipc_channels_for_serialization.len(); 1025 | os_ipc_channels_for_serialization.push(OsIpcChannel::Receiver(os_receiver.consume())); 1026 | index 1027 | }); 1028 | index.serialize(serializer) 1029 | } 1030 | 1031 | fn deserialize_os_ipc_receiver<'de, D>(deserializer: D) -> Result 1032 | where 1033 | D: Deserializer<'de>, 1034 | { 1035 | let index: usize = Deserialize::deserialize(deserializer)?; 1036 | 1037 | OS_IPC_CHANNELS_FOR_DESERIALIZATION.with(|os_ipc_channels_for_deserialization| { 1038 | // FIXME(pcwalton): This could panic if the data was corrupt and the index was out 1039 | // of bounds. We should return an `Err` result instead. 1040 | Ok(os_ipc_channels_for_deserialization.borrow_mut()[index].to_receiver()) 1041 | }) 1042 | } 1043 | -------------------------------------------------------------------------------- /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::slice; 21 | use std::sync::{Arc, LazyLock, Mutex}; 22 | use std::time::Duration; 23 | use std::usize; 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: 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: RefCell>, 127 | } 128 | 129 | impl PartialEq for OsIpcSender { 130 | fn eq(&self, other: &OsIpcSender) -> bool { 131 | &*self.sender.borrow() as *const _ == &*other.sender.borrow() as *const _ 132 | } 133 | } 134 | 135 | impl OsIpcSender { 136 | fn new(sender: Sender) -> OsIpcSender { 137 | OsIpcSender { 138 | sender: RefCell::new(sender), 139 | } 140 | } 141 | 142 | pub fn connect(name: String) -> Result { 143 | let record = ONE_SHOT_SERVERS.lock().unwrap().get(&name).unwrap().clone(); 144 | record.connect(); 145 | Ok(record.sender) 146 | } 147 | 148 | pub fn get_max_fragment_size() -> usize { 149 | usize::MAX 150 | } 151 | 152 | pub fn send( 153 | &self, 154 | data: &[u8], 155 | ports: Vec, 156 | shared_memory_regions: Vec, 157 | ) -> Result<(), ChannelError> { 158 | let os_ipc_channels = ports.into_iter().map(OsOpaqueIpcChannel::new).collect(); 159 | let ipc_message = IpcMessage::new(data.to_vec(), os_ipc_channels, shared_memory_regions); 160 | Ok(self 161 | .sender 162 | .borrow() 163 | .send(ChannelMessage(ipc_message)) 164 | .map_err(|_| ChannelError::BrokenPipeError)?) 165 | } 166 | } 167 | 168 | pub struct OsIpcReceiverSet { 169 | incrementor: RangeFrom, 170 | receiver_ids: Vec, 171 | receivers: Vec, 172 | } 173 | 174 | impl OsIpcReceiverSet { 175 | pub fn new() -> Result { 176 | Ok(OsIpcReceiverSet { 177 | incrementor: 0.., 178 | receiver_ids: vec![], 179 | receivers: vec![], 180 | }) 181 | } 182 | 183 | pub fn add(&mut self, receiver: OsIpcReceiver) -> Result { 184 | let last_index = self.incrementor.next().unwrap(); 185 | self.receiver_ids.push(last_index); 186 | self.receivers.push(receiver.consume()); 187 | Ok(last_index) 188 | } 189 | 190 | pub fn select(&mut self) -> Result, ChannelError> { 191 | if self.receivers.is_empty() { 192 | return Err(ChannelError::UnknownError); 193 | } 194 | 195 | struct Remove(usize, u64); 196 | 197 | // FIXME: Remove early returns and explicitly drop `borrows` when lifetimes are non-lexical 198 | let Remove(r_index, r_id) = { 199 | let borrows: Vec<_> = self 200 | .receivers 201 | .iter() 202 | .map(|r| Ref::map(r.receiver.borrow(), |o| o.as_ref().unwrap())) 203 | .collect(); 204 | 205 | let mut select = Select::new(); 206 | for r in &borrows { 207 | select.recv(&r); 208 | } 209 | let res = select.select(); 210 | let receiver_index = res.index(); 211 | let receiver_id = self.receiver_ids[receiver_index]; 212 | if let Ok(ChannelMessage(ipc_message)) = res.recv(&borrows[receiver_index as usize]) { 213 | return Ok(vec![OsIpcSelectionResult::DataReceived( 214 | receiver_id, 215 | ipc_message, 216 | )]); 217 | } else { 218 | Remove(receiver_index, receiver_id) 219 | } 220 | }; 221 | self.receivers.remove(r_index); 222 | self.receiver_ids.remove(r_index); 223 | Ok(vec![OsIpcSelectionResult::ChannelClosed(r_id)]) 224 | } 225 | } 226 | 227 | pub enum OsIpcSelectionResult { 228 | DataReceived(u64, IpcMessage), 229 | ChannelClosed(u64), 230 | } 231 | 232 | impl OsIpcSelectionResult { 233 | pub fn unwrap(self) -> (u64, IpcMessage) { 234 | match self { 235 | OsIpcSelectionResult::DataReceived(id, ipc_message) => (id, ipc_message), 236 | OsIpcSelectionResult::ChannelClosed(id) => { 237 | panic!( 238 | "OsIpcSelectionResult::unwrap(): receiver ID {} was closed!", 239 | id 240 | ) 241 | }, 242 | } 243 | } 244 | } 245 | 246 | pub struct OsIpcOneShotServer { 247 | receiver: OsIpcReceiver, 248 | name: String, 249 | } 250 | 251 | impl OsIpcOneShotServer { 252 | pub fn new() -> Result<(OsIpcOneShotServer, String), ChannelError> { 253 | let (sender, receiver) = channel()?; 254 | 255 | let name = Uuid::new_v4().to_string(); 256 | let record = ServerRecord::new(sender); 257 | ONE_SHOT_SERVERS 258 | .lock() 259 | .unwrap() 260 | .insert(name.clone(), record); 261 | Ok(( 262 | OsIpcOneShotServer { 263 | receiver: receiver, 264 | name: name.clone(), 265 | }, 266 | name.clone(), 267 | )) 268 | } 269 | 270 | pub fn accept(self) -> Result<(OsIpcReceiver, IpcMessage), ChannelError> { 271 | let record = ONE_SHOT_SERVERS 272 | .lock() 273 | .unwrap() 274 | .get(&self.name) 275 | .unwrap() 276 | .clone(); 277 | record.accept(); 278 | ONE_SHOT_SERVERS.lock().unwrap().remove(&self.name).unwrap(); 279 | let ipc_message = self.receiver.recv()?; 280 | Ok((self.receiver, ipc_message)) 281 | } 282 | } 283 | 284 | #[derive(PartialEq, Debug)] 285 | pub enum OsIpcChannel { 286 | Sender(OsIpcSender), 287 | Receiver(OsIpcReceiver), 288 | } 289 | 290 | #[derive(PartialEq, Debug)] 291 | pub struct OsOpaqueIpcChannel { 292 | channel: RefCell>, 293 | } 294 | 295 | impl OsOpaqueIpcChannel { 296 | fn new(channel: OsIpcChannel) -> OsOpaqueIpcChannel { 297 | OsOpaqueIpcChannel { 298 | channel: RefCell::new(Some(channel)), 299 | } 300 | } 301 | 302 | pub fn to_receiver(&self) -> OsIpcReceiver { 303 | match self.channel.borrow_mut().take().unwrap() { 304 | OsIpcChannel::Sender(_) => panic!("Opaque channel is not a receiver!"), 305 | OsIpcChannel::Receiver(r) => r, 306 | } 307 | } 308 | 309 | pub fn to_sender(&mut self) -> OsIpcSender { 310 | match self.channel.borrow_mut().take().unwrap() { 311 | OsIpcChannel::Sender(s) => s, 312 | OsIpcChannel::Receiver(_) => panic!("Opaque channel is not a sender!"), 313 | } 314 | } 315 | } 316 | 317 | pub struct OsIpcSharedMemory { 318 | ptr: *mut u8, 319 | length: usize, 320 | data: Arc>, 321 | } 322 | 323 | unsafe impl Send for OsIpcSharedMemory {} 324 | unsafe impl Sync for OsIpcSharedMemory {} 325 | 326 | impl Clone for OsIpcSharedMemory { 327 | fn clone(&self) -> OsIpcSharedMemory { 328 | OsIpcSharedMemory { 329 | ptr: self.ptr, 330 | length: self.length, 331 | data: self.data.clone(), 332 | } 333 | } 334 | } 335 | 336 | impl PartialEq for OsIpcSharedMemory { 337 | fn eq(&self, other: &OsIpcSharedMemory) -> bool { 338 | **self == **other 339 | } 340 | } 341 | 342 | impl Debug for OsIpcSharedMemory { 343 | fn fmt(&self, formatter: &mut Formatter) -> Result<(), fmt::Error> { 344 | (**self).fmt(formatter) 345 | } 346 | } 347 | 348 | impl Deref for OsIpcSharedMemory { 349 | type Target = [u8]; 350 | 351 | #[inline] 352 | fn deref(&self) -> &[u8] { 353 | if self.ptr.is_null() { 354 | panic!("attempted to access a consumed `OsIpcSharedMemory`") 355 | } 356 | unsafe { slice::from_raw_parts(self.ptr, self.length) } 357 | } 358 | } 359 | 360 | impl OsIpcSharedMemory { 361 | #[inline] 362 | pub unsafe fn deref_mut(&mut self) -> &mut [u8] { 363 | if self.ptr.is_null() { 364 | panic!("attempted to access a consumed `OsIpcSharedMemory`") 365 | } 366 | unsafe { slice::from_raw_parts_mut(self.ptr, self.length) } 367 | } 368 | } 369 | 370 | impl OsIpcSharedMemory { 371 | pub fn from_byte(byte: u8, length: usize) -> OsIpcSharedMemory { 372 | let mut v = Arc::new(vec![byte; length]); 373 | OsIpcSharedMemory { 374 | ptr: Arc::get_mut(&mut v).unwrap().as_mut_ptr(), 375 | length: length, 376 | data: v, 377 | } 378 | } 379 | 380 | pub fn from_bytes(bytes: &[u8]) -> OsIpcSharedMemory { 381 | let mut v = Arc::new(bytes.to_vec()); 382 | OsIpcSharedMemory { 383 | ptr: Arc::get_mut(&mut v).unwrap().as_mut_ptr(), 384 | length: v.len(), 385 | data: v, 386 | } 387 | } 388 | } 389 | 390 | #[derive(Debug, PartialEq)] 391 | pub enum ChannelError { 392 | ChannelClosedError, 393 | BrokenPipeError, 394 | ChannelEmpty, 395 | UnknownError, 396 | } 397 | 398 | impl ChannelError { 399 | #[allow(dead_code)] 400 | pub fn channel_is_closed(&self) -> bool { 401 | *self == ChannelError::ChannelClosedError 402 | } 403 | } 404 | 405 | impl fmt::Display for ChannelError { 406 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 407 | match *self { 408 | ChannelError::ChannelClosedError => write!(fmt, "channel closed"), 409 | ChannelError::BrokenPipeError => write!(fmt, "broken pipe"), 410 | ChannelError::ChannelEmpty => write!(fmt, "channel empty"), 411 | ChannelError::UnknownError => write!(fmt, "unknown error"), 412 | } 413 | } 414 | } 415 | 416 | impl StdError for ChannelError {} 417 | 418 | impl From for bincode::Error { 419 | fn from(crossbeam_error: ChannelError) -> Self { 420 | io::Error::from(crossbeam_error).into() 421 | } 422 | } 423 | 424 | impl From for ipc::IpcError { 425 | fn from(error: ChannelError) -> Self { 426 | match error { 427 | ChannelError::ChannelClosedError => ipc::IpcError::Disconnected, 428 | e => ipc::IpcError::Bincode(io::Error::from(e).into()), 429 | } 430 | } 431 | } 432 | 433 | impl From for ipc::TryRecvError { 434 | fn from(error: ChannelError) -> Self { 435 | match error { 436 | ChannelError::ChannelClosedError => { 437 | ipc::TryRecvError::IpcError(ipc::IpcError::Disconnected) 438 | }, 439 | ChannelError::ChannelEmpty => ipc::TryRecvError::Empty, 440 | e => ipc::TryRecvError::IpcError(ipc::IpcError::Bincode(io::Error::from(e).into())), 441 | } 442 | } 443 | } 444 | 445 | impl From for io::Error { 446 | fn from(crossbeam_error: ChannelError) -> io::Error { 447 | match crossbeam_error { 448 | ChannelError::ChannelClosedError => io::Error::new( 449 | io::ErrorKind::ConnectionReset, 450 | "crossbeam-channel sender closed", 451 | ), 452 | ChannelError::ChannelEmpty => io::Error::new( 453 | io::ErrorKind::ConnectionReset, 454 | "crossbeam-channel receiver has no received messages", 455 | ), 456 | ChannelError::BrokenPipeError => io::Error::new( 457 | io::ErrorKind::BrokenPipe, 458 | "crossbeam-channel receiver closed", 459 | ), 460 | ChannelError::UnknownError => { 461 | io::Error::new(io::ErrorKind::Other, "Other crossbeam-channel error") 462 | }, 463 | } 464 | } 465 | } 466 | -------------------------------------------------------------------------------- /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::marker::PhantomData; 33 | use std::mem; 34 | use std::ops::{Deref, RangeFrom}; 35 | use std::os::fd::RawFd; 36 | use std::ptr; 37 | use std::slice; 38 | use std::sync::atomic::{AtomicUsize, Ordering}; 39 | use std::sync::{Arc, LazyLock}; 40 | use std::thread; 41 | use std::time::{Duration, UNIX_EPOCH}; 42 | use tempfile::{Builder, TempDir}; 43 | 44 | const MAX_FDS_IN_CMSG: u32 = 64; 45 | 46 | // The value Linux returns for SO_SNDBUF 47 | // is not the size we are actually allowed to use... 48 | // Empirically, we have to deduct 32 bytes from that. 49 | const RESERVED_SIZE: usize = 32; 50 | 51 | #[cfg(any(target_os = "linux", target_os = "illumos"))] 52 | const SOCK_FLAGS: c_int = libc::SOCK_CLOEXEC; 53 | #[cfg(not(any(target_os = "linux", target_os = "illumos")))] 54 | const SOCK_FLAGS: c_int = 0; 55 | 56 | #[cfg(any(target_os = "linux", target_os = "illumos"))] 57 | const RECVMSG_FLAGS: c_int = libc::MSG_CMSG_CLOEXEC; 58 | #[cfg(not(any(target_os = "linux", target_os = "illumos")))] 59 | const RECVMSG_FLAGS: c_int = 0; 60 | 61 | #[cfg(target_env = "gnu")] 62 | type IovLen = usize; 63 | #[cfg(target_env = "gnu")] 64 | type MsgControlLen = size_t; 65 | 66 | #[cfg(not(target_env = "gnu"))] 67 | type IovLen = i32; 68 | #[cfg(not(target_env = "gnu"))] 69 | type MsgControlLen = socklen_t; 70 | 71 | unsafe fn new_sockaddr_un(path: *const c_char) -> (sockaddr_un, usize) { 72 | let mut sockaddr: sockaddr_un = mem::zeroed(); 73 | libc::strncpy( 74 | sockaddr.sun_path.as_mut_ptr(), 75 | path, 76 | sockaddr.sun_path.len() - 1, 77 | ); 78 | sockaddr.sun_family = libc::AF_UNIX as sa_family_t; 79 | (sockaddr, mem::size_of::()) 80 | } 81 | 82 | static SYSTEM_SENDBUF_SIZE: LazyLock = LazyLock::new(|| { 83 | let (tx, _) = channel().expect("Failed to obtain a socket for checking maximum send size"); 84 | tx.get_system_sendbuf_size() 85 | .expect("Failed to obtain maximum send size for socket") 86 | }); 87 | 88 | // The pid of the current process which is used to create unique IDs 89 | static PID: LazyLock = LazyLock::new(std::process::id); 90 | 91 | // A global count used to create unique IDs 92 | static SHM_COUNT: AtomicUsize = AtomicUsize::new(0); 93 | 94 | pub fn channel() -> Result<(OsIpcSender, OsIpcReceiver), UnixError> { 95 | let mut results = [0, 0]; 96 | unsafe { 97 | if socketpair( 98 | libc::AF_UNIX, 99 | SOCK_SEQPACKET | SOCK_FLAGS, 100 | 0, 101 | &mut results[0], 102 | ) >= 0 103 | { 104 | Ok(( 105 | OsIpcSender::from_fd(results[0]), 106 | OsIpcReceiver::from_fd(results[1]), 107 | )) 108 | } else { 109 | Err(UnixError::last()) 110 | } 111 | } 112 | } 113 | 114 | #[derive(Clone, Copy)] 115 | struct PollEntry { 116 | pub id: u64, 117 | pub fd: RawFd, 118 | } 119 | 120 | #[derive(PartialEq, Debug)] 121 | pub struct OsIpcReceiver { 122 | fd: Cell, 123 | } 124 | 125 | impl Drop for OsIpcReceiver { 126 | fn drop(&mut self) { 127 | unsafe { 128 | let fd = self.fd.get(); 129 | if fd >= 0 { 130 | let result = libc::close(fd); 131 | assert!( 132 | thread::panicking() || result == 0, 133 | "closed receiver (fd: {}): {}", 134 | fd, 135 | UnixError::last(), 136 | ); 137 | } 138 | } 139 | } 140 | } 141 | 142 | impl OsIpcReceiver { 143 | fn from_fd(fd: c_int) -> OsIpcReceiver { 144 | OsIpcReceiver { fd: Cell::new(fd) } 145 | } 146 | 147 | fn consume_fd(&self) -> c_int { 148 | let fd = self.fd.get(); 149 | self.fd.set(-1); 150 | fd 151 | } 152 | 153 | pub fn consume(&self) -> OsIpcReceiver { 154 | OsIpcReceiver::from_fd(self.consume_fd()) 155 | } 156 | 157 | #[allow(clippy::type_complexity)] 158 | pub fn recv(&self) -> Result { 159 | recv(self.fd.get(), BlockingMode::Blocking) 160 | } 161 | 162 | #[allow(clippy::type_complexity)] 163 | pub fn try_recv(&self) -> Result { 164 | recv(self.fd.get(), BlockingMode::Nonblocking) 165 | } 166 | 167 | #[allow(clippy::type_complexity)] 168 | pub fn try_recv_timeout(&self, duration: Duration) -> Result { 169 | recv(self.fd.get(), BlockingMode::Timeout(duration)) 170 | } 171 | } 172 | 173 | #[derive(PartialEq, Debug)] 174 | struct SharedFileDescriptor(c_int); 175 | 176 | impl Drop for SharedFileDescriptor { 177 | fn drop(&mut self) { 178 | unsafe { 179 | let result = libc::close(self.0); 180 | assert!(thread::panicking() || result == 0); 181 | } 182 | } 183 | } 184 | 185 | #[derive(PartialEq, Debug, Clone)] 186 | pub struct OsIpcSender { 187 | fd: Arc, 188 | // Make sure this is `!Sync`, to match `mpsc::Sender`; and to discourage sharing references. 189 | // 190 | // (Rather, senders should just be cloned, as they are shared internally anyway -- 191 | // another layer of sharing only adds unnecessary overhead...) 192 | nosync_marker: PhantomData>, 193 | } 194 | 195 | impl OsIpcSender { 196 | fn from_fd(fd: c_int) -> OsIpcSender { 197 | OsIpcSender { 198 | fd: Arc::new(SharedFileDescriptor(fd)), 199 | nosync_marker: PhantomData, 200 | } 201 | } 202 | 203 | /// Maximum size of the kernel buffer used for transfers over this channel. 204 | /// 205 | /// Note: This is *not* the actual maximal packet size we are allowed to use... 206 | /// Some of it is reserved by the kernel for bookkeeping. 207 | fn get_system_sendbuf_size(&self) -> Result { 208 | unsafe { 209 | let mut socket_sendbuf_size: c_int = 0; 210 | let mut socket_sendbuf_size_len = mem::size_of::() as socklen_t; 211 | if getsockopt( 212 | self.fd.0, 213 | libc::SOL_SOCKET, 214 | libc::SO_SNDBUF, 215 | &mut socket_sendbuf_size as *mut _ as *mut c_void, 216 | &mut socket_sendbuf_size_len as *mut socklen_t, 217 | ) < 0 218 | { 219 | return Err(UnixError::last()); 220 | } 221 | Ok(socket_sendbuf_size.try_into().unwrap()) 222 | } 223 | } 224 | 225 | /// Calculate maximum payload data size per fragment. 226 | /// 227 | /// It is the total size of the kernel buffer, minus the part reserved by the kernel. 228 | /// 229 | /// The `sendbuf_size` passed in should usually be the maximum kernel buffer size, 230 | /// i.e. the value of *SYSTEM_SENDBUF_SIZE -- 231 | /// except after getting ENOBUFS, in which case it needs to be reduced. 232 | fn fragment_size(sendbuf_size: usize) -> usize { 233 | sendbuf_size - RESERVED_SIZE 234 | } 235 | 236 | /// Calculate maximum payload data size of first fragment. 237 | /// 238 | /// This one is smaller than regular fragments, because it carries the message (size) header. 239 | fn first_fragment_size(sendbuf_size: usize) -> usize { 240 | (Self::fragment_size(sendbuf_size) - mem::size_of::()) & (!8usize + 1) 241 | // Ensure optimal alignment. 242 | } 243 | 244 | /// Maximum data size that can be transferred over this channel in a single packet. 245 | /// 246 | /// This is the size of the main data chunk only -- 247 | /// it's independent of any auxiliary data (FDs) transferred along with it. 248 | /// 249 | /// A send on this channel won't block for transfers up to this size 250 | /// under normal circumstances. 251 | /// (It might still block if heavy memory pressure causes ENOBUFS, 252 | /// forcing us to reduce the packet size.) 253 | pub fn get_max_fragment_size() -> usize { 254 | Self::first_fragment_size(*SYSTEM_SENDBUF_SIZE) 255 | } 256 | 257 | pub fn send( 258 | &self, 259 | data: &[u8], 260 | channels: Vec, 261 | shared_memory_regions: Vec, 262 | ) -> Result<(), UnixError> { 263 | let mut fds = Vec::new(); 264 | for channel in channels.iter() { 265 | fds.push(channel.fd()); 266 | } 267 | for shared_memory_region in shared_memory_regions.iter() { 268 | fds.push(shared_memory_region.store.fd()); 269 | } 270 | 271 | // `len` is the total length of the message. 272 | // Its value will be sent as a message header before the payload data. 273 | // 274 | // Not to be confused with the length of the data to send in this packet 275 | // (i.e. the length of the data buffer passed in), 276 | // which in a fragmented send will be smaller than the total message length. 277 | fn send_first_fragment( 278 | sender_fd: c_int, 279 | fds: &[c_int], 280 | data_buffer: &[u8], 281 | len: usize, 282 | ) -> Result<(), UnixError> { 283 | let result = unsafe { 284 | let cmsg_length = mem::size_of_val(fds) as c_uint; 285 | let (cmsg_buffer, cmsg_space) = if cmsg_length > 0 { 286 | let cmsg_buffer = 287 | libc::malloc(CMSG_SPACE(cmsg_length) as usize) as *mut cmsghdr; 288 | if cmsg_buffer.is_null() { 289 | return Err(UnixError::last()); 290 | } 291 | (*cmsg_buffer).cmsg_len = CMSG_LEN(cmsg_length) as MsgControlLen; 292 | (*cmsg_buffer).cmsg_level = libc::SOL_SOCKET; 293 | (*cmsg_buffer).cmsg_type = libc::SCM_RIGHTS; 294 | 295 | ptr::copy_nonoverlapping( 296 | fds.as_ptr(), 297 | CMSG_DATA(cmsg_buffer) as *mut c_int, 298 | fds.len(), 299 | ); 300 | (cmsg_buffer, CMSG_SPACE(cmsg_length)) 301 | } else { 302 | (ptr::null_mut(), 0) 303 | }; 304 | 305 | let mut iovec = [ 306 | // First fragment begins with a header recording the total data length. 307 | // 308 | // The receiver uses this to determine 309 | // whether it already got the entire message, 310 | // or needs to receive additional fragments -- and if so, how much. 311 | iovec { 312 | iov_base: &len as *const _ as *mut c_void, 313 | iov_len: mem::size_of_val(&len), 314 | }, 315 | iovec { 316 | iov_base: data_buffer.as_ptr() as *mut c_void, 317 | iov_len: data_buffer.len(), 318 | }, 319 | ]; 320 | 321 | let msghdr = new_msghdr(&mut iovec, cmsg_buffer, cmsg_space as MsgControlLen); 322 | let result = sendmsg(sender_fd, &msghdr, 0); 323 | libc::free(cmsg_buffer as *mut c_void); 324 | result 325 | }; 326 | 327 | if result > 0 { 328 | Ok(()) 329 | } else { 330 | Err(UnixError::last()) 331 | } 332 | } 333 | 334 | fn send_followup_fragment(sender_fd: c_int, data_buffer: &[u8]) -> Result<(), UnixError> { 335 | let result = unsafe { 336 | libc::send( 337 | sender_fd, 338 | data_buffer.as_ptr() as *const c_void, 339 | data_buffer.len(), 340 | 0, 341 | ) 342 | }; 343 | 344 | if result > 0 { 345 | Ok(()) 346 | } else { 347 | Err(UnixError::last()) 348 | } 349 | } 350 | 351 | let mut sendbuf_size = *SYSTEM_SENDBUF_SIZE; 352 | 353 | /// Reduce send buffer size after getting ENOBUFS, 354 | /// i.e. when the kernel failed to allocate a large enough buffer. 355 | /// 356 | /// (If the buffer already was significantly smaller 357 | /// than the memory page size though, 358 | /// if means something else must have gone wrong; 359 | /// so there is no point in further downsizing, 360 | /// and we error out instead.) 361 | fn downsize(sendbuf_size: &mut usize, sent_size: usize) -> Result<(), ()> { 362 | if sent_size > 2000 { 363 | *sendbuf_size /= 2; 364 | // Make certain we end up with less than what we tried before... 365 | if *sendbuf_size >= sent_size { 366 | *sendbuf_size = sent_size / 2; 367 | } 368 | Ok(()) 369 | } else { 370 | Err(()) 371 | } 372 | } 373 | 374 | // If the message is small enough, try sending it in a single fragment. 375 | if data.len() <= Self::get_max_fragment_size() { 376 | match send_first_fragment(self.fd.0, &fds[..], data, data.len()) { 377 | Ok(_) => return Ok(()), 378 | Err(error) => { 379 | // ENOBUFS means the kernel failed to allocate a buffer large enough 380 | // to actually transfer the message, 381 | // although the message was small enough to fit the maximum send size -- 382 | // so we have to proceed with a fragmented send nevertheless, 383 | // using a reduced send buffer size. 384 | // 385 | // Any other errors we might get here are non-recoverable. 386 | if !(matches!(error, UnixError::Errno(libc::ENOBUFS)) 387 | && downsize(&mut sendbuf_size, data.len()).is_ok()) 388 | { 389 | return Err(error); 390 | } 391 | }, 392 | } 393 | } 394 | 395 | // The packet is too big. Fragmentation time! 396 | // 397 | // Create dedicated channel to send all but the first fragment. 398 | // This way we avoid fragments of different messages interleaving in the receiver. 399 | // 400 | // The receiver end of the channel is sent with the first fragment 401 | // along any other file descriptors that are to be transferred in the message. 402 | let (dedicated_tx, dedicated_rx) = channel()?; 403 | // Extract FD handle without consuming the Receiver, so the FD doesn't get closed. 404 | fds.push(dedicated_rx.fd.get()); 405 | 406 | // Split up the packet into fragments. 407 | let mut byte_position = 0; 408 | while byte_position < data.len() { 409 | let end_byte_position; 410 | let result = if byte_position == 0 { 411 | // First fragment. No offset; but contains message header (total size). 412 | // The auxiliary data (FDs) is also sent along with this one. 413 | 414 | // This fragment always uses the full allowable buffer size. 415 | end_byte_position = Self::first_fragment_size(sendbuf_size); 416 | send_first_fragment(self.fd.0, &fds[..], &data[..end_byte_position], data.len()) 417 | } else { 418 | // Followup fragment. No header; but offset by amount of data already sent. 419 | 420 | end_byte_position = cmp::min( 421 | byte_position + Self::fragment_size(sendbuf_size), 422 | data.len(), 423 | ); 424 | send_followup_fragment(dedicated_tx.fd.0, &data[byte_position..end_byte_position]) 425 | }; 426 | 427 | if let Err(error) = result { 428 | if matches!(error, UnixError::Errno(libc::ENOBUFS)) 429 | && downsize(&mut sendbuf_size, end_byte_position - byte_position).is_ok() 430 | { 431 | // If the kernel failed to allocate a buffer large enough for the packet, 432 | // retry with a smaller size (if possible). 433 | continue; 434 | } else { 435 | return Err(error); 436 | } 437 | } 438 | 439 | byte_position = end_byte_position; 440 | } 441 | 442 | Ok(()) 443 | } 444 | 445 | pub fn connect(name: String) -> Result { 446 | let name = CString::new(name).unwrap(); 447 | unsafe { 448 | let fd = libc::socket(libc::AF_UNIX, SOCK_SEQPACKET | SOCK_FLAGS, 0); 449 | let (sockaddr, len) = new_sockaddr_un(name.as_ptr()); 450 | if libc::connect( 451 | fd, 452 | &sockaddr as *const _ as *const sockaddr, 453 | len as socklen_t, 454 | ) < 0 455 | { 456 | return Err(UnixError::last()); 457 | } 458 | 459 | Ok(OsIpcSender::from_fd(fd)) 460 | } 461 | } 462 | } 463 | 464 | #[derive(PartialEq, Debug)] 465 | pub enum OsIpcChannel { 466 | Sender(OsIpcSender), 467 | Receiver(OsIpcReceiver), 468 | } 469 | 470 | impl OsIpcChannel { 471 | fn fd(&self) -> c_int { 472 | match *self { 473 | OsIpcChannel::Sender(ref sender) => sender.fd.0, 474 | OsIpcChannel::Receiver(ref receiver) => receiver.fd.get(), 475 | } 476 | } 477 | } 478 | 479 | pub struct OsIpcReceiverSet { 480 | incrementor: RangeFrom, 481 | poll: Poll, 482 | pollfds: HashMap>, 483 | events: Events, 484 | } 485 | 486 | impl Drop for OsIpcReceiverSet { 487 | fn drop(&mut self) { 488 | for &PollEntry { id: _, fd } in self.pollfds.values() { 489 | let result = unsafe { libc::close(fd) }; 490 | assert!(thread::panicking() || result == 0); 491 | } 492 | } 493 | } 494 | 495 | impl OsIpcReceiverSet { 496 | pub fn new() -> Result { 497 | let fnv = BuildHasherDefault::::default(); 498 | Ok(OsIpcReceiverSet { 499 | incrementor: 0.., 500 | poll: Poll::new()?, 501 | pollfds: HashMap::with_hasher(fnv), 502 | events: Events::with_capacity(10), 503 | }) 504 | } 505 | 506 | pub fn add(&mut self, receiver: OsIpcReceiver) -> Result { 507 | let last_index = self.incrementor.next().unwrap(); 508 | let fd = receiver.consume_fd(); 509 | let fd_token = Token(fd as usize); 510 | let poll_entry = PollEntry { id: last_index, fd }; 511 | self.poll 512 | .registry() 513 | .register(&mut SourceFd(&fd), fd_token, Interest::READABLE)?; 514 | self.pollfds.insert(fd_token, poll_entry); 515 | Ok(last_index) 516 | } 517 | 518 | pub fn select(&mut self) -> Result, UnixError> { 519 | // Poll until we receive at least one event. 520 | loop { 521 | match self.poll.poll(&mut self.events, None) { 522 | Ok(()) if !self.events.is_empty() => break, 523 | Ok(()) => {}, 524 | Err(ref error) => { 525 | if error.kind() != io::ErrorKind::Interrupted { 526 | return Err(UnixError::last()); 527 | } 528 | }, 529 | } 530 | 531 | if !self.events.is_empty() { 532 | break; 533 | } 534 | } 535 | 536 | let mut selection_results = Vec::new(); 537 | for event in self.events.iter() { 538 | // We only register this `Poll` for readable events. 539 | assert!(event.is_readable()); 540 | 541 | let event_token = event.token(); 542 | let poll_entry = self 543 | .pollfds 544 | .get(&event_token) 545 | .expect("Got event for unknown token.") 546 | .clone(); 547 | loop { 548 | match recv(poll_entry.fd, BlockingMode::Nonblocking) { 549 | Ok(ipc_message) => { 550 | selection_results.push(OsIpcSelectionResult::DataReceived( 551 | poll_entry.id, 552 | ipc_message, 553 | )); 554 | }, 555 | Err(err) if err.channel_is_closed() => { 556 | self.pollfds.remove(&event_token).unwrap(); 557 | self.poll 558 | .registry() 559 | .deregister(&mut SourceFd(&poll_entry.fd)) 560 | .unwrap(); 561 | unsafe { 562 | libc::close(poll_entry.fd); 563 | } 564 | 565 | selection_results.push(OsIpcSelectionResult::ChannelClosed(poll_entry.id)); 566 | break; 567 | }, 568 | Err(UnixError::Errno(code)) if code == EWOULDBLOCK => { 569 | // We tried to read another message from the file descriptor and 570 | // it would have blocked, so we have exhausted all of the data 571 | // pending to read. 572 | break; 573 | }, 574 | Err(err) => return Err(err), 575 | } 576 | } 577 | } 578 | 579 | Ok(selection_results) 580 | } 581 | } 582 | 583 | pub enum OsIpcSelectionResult { 584 | DataReceived(u64, IpcMessage), 585 | ChannelClosed(u64), 586 | } 587 | 588 | impl OsIpcSelectionResult { 589 | pub fn unwrap(self) -> (u64, IpcMessage) { 590 | match self { 591 | OsIpcSelectionResult::DataReceived(id, ipc_message) => (id, ipc_message), 592 | OsIpcSelectionResult::ChannelClosed(id) => { 593 | panic!( 594 | "OsIpcSelectionResult::unwrap(): receiver ID {} was closed!", 595 | id 596 | ) 597 | }, 598 | } 599 | } 600 | } 601 | 602 | #[derive(PartialEq, Debug)] 603 | pub struct OsOpaqueIpcChannel { 604 | fd: c_int, 605 | } 606 | 607 | impl Drop for OsOpaqueIpcChannel { 608 | fn drop(&mut self) { 609 | // Make sure we don't leak! 610 | // 611 | // The `OsOpaqueIpcChannel` objects should always be used, 612 | // i.e. converted with `to_sender()` or `to_receiver()` -- 613 | // so the value should already be unset before the object gets dropped. 614 | debug_assert!(self.fd == -1); 615 | } 616 | } 617 | 618 | impl OsOpaqueIpcChannel { 619 | fn from_fd(fd: c_int) -> OsOpaqueIpcChannel { 620 | OsOpaqueIpcChannel { fd } 621 | } 622 | 623 | pub fn to_sender(&mut self) -> OsIpcSender { 624 | OsIpcSender::from_fd(mem::replace(&mut self.fd, -1)) 625 | } 626 | 627 | pub fn to_receiver(&mut self) -> OsIpcReceiver { 628 | OsIpcReceiver::from_fd(mem::replace(&mut self.fd, -1)) 629 | } 630 | } 631 | 632 | pub struct OsIpcOneShotServer { 633 | fd: c_int, 634 | 635 | // Object representing the temporary directory the socket was created in. 636 | // The directory is automatically deleted (along with the socket inside it) 637 | // when this field is dropped. 638 | _temp_dir: TempDir, 639 | } 640 | 641 | impl Drop for OsIpcOneShotServer { 642 | fn drop(&mut self) { 643 | unsafe { 644 | let result = libc::close(self.fd); 645 | assert!(thread::panicking() || result == 0); 646 | } 647 | } 648 | } 649 | 650 | impl OsIpcOneShotServer { 651 | pub fn new() -> Result<(OsIpcOneShotServer, String), UnixError> { 652 | unsafe { 653 | let fd = libc::socket(libc::AF_UNIX, SOCK_SEQPACKET | SOCK_FLAGS, 0); 654 | let temp_dir = Builder::new().tempdir()?; 655 | let socket_path = temp_dir.path().join("socket"); 656 | let path_string = socket_path.to_str().unwrap(); 657 | 658 | let path_c_string = CString::new(path_string).unwrap(); 659 | let (sockaddr, len) = new_sockaddr_un(path_c_string.as_ptr()); 660 | if libc::bind( 661 | fd, 662 | &sockaddr as *const _ as *const sockaddr, 663 | len as socklen_t, 664 | ) != 0 665 | { 666 | return Err(UnixError::last()); 667 | } 668 | 669 | if libc::listen(fd, 10) != 0 { 670 | return Err(UnixError::last()); 671 | } 672 | 673 | Ok(( 674 | OsIpcOneShotServer { 675 | fd, 676 | _temp_dir: temp_dir, 677 | }, 678 | path_string.to_string(), 679 | )) 680 | } 681 | } 682 | 683 | #[allow(clippy::type_complexity)] 684 | pub fn accept(self) -> Result<(OsIpcReceiver, IpcMessage), UnixError> { 685 | unsafe { 686 | let sockaddr: *mut sockaddr = ptr::null_mut(); 687 | let sockaddr_len: *mut socklen_t = ptr::null_mut(); 688 | let client_fd = libc::accept(self.fd, sockaddr, sockaddr_len); 689 | if client_fd < 0 { 690 | return Err(UnixError::last()); 691 | } 692 | make_socket_lingering(client_fd)?; 693 | 694 | let receiver = OsIpcReceiver::from_fd(client_fd); 695 | let ipc_message = receiver.recv()?; 696 | Ok((receiver, ipc_message)) 697 | } 698 | } 699 | } 700 | 701 | // Make sure that the kernel doesn't return errors to readers if there's still data left after we 702 | // close our end. 703 | // 704 | // See, for example, https://github.com/servo/ipc-channel/issues/29 705 | fn make_socket_lingering(sockfd: c_int) -> Result<(), UnixError> { 706 | let linger = linger { 707 | l_onoff: 1, 708 | l_linger: 30, 709 | }; 710 | let err = unsafe { 711 | setsockopt( 712 | sockfd, 713 | SOL_SOCKET, 714 | SO_LINGER, 715 | &linger as *const _ as *const c_void, 716 | mem::size_of::() as socklen_t, 717 | ) 718 | }; 719 | if err < 0 { 720 | let error = UnixError::last(); 721 | if let UnixError::Errno(libc::EINVAL) = error { 722 | // If the other side of the connection is already closed, POSIX.1-2024 (and earlier 723 | // versions) require that setsockopt return EINVAL [1]. This is a bit unfortunate 724 | // because SO_LINGER for a closed socket is logically a no-op, which is why some OSes 725 | // like Linux don't follow this part of the spec. But other OSes like illumos do return 726 | // EINVAL here. 727 | // 728 | // SO_LINGER is widely understood and EINVAL should not occur for any other reason, so 729 | // accept those errors. 730 | // 731 | // Another option would be to call make_socket_lingering on the initial socket created 732 | // by libc::socket, but whether accept inherits a particular option is 733 | // implementation-defined [2]. This means that special-casing EINVAL is the most 734 | // portable thing to do. 735 | // 736 | // [1] https://pubs.opengroup.org/onlinepubs/9799919799/functions/setsockopt.html: 737 | // "[EINVAL] The specified option is invalid at the specified socket level or the 738 | // socket has been shut down." 739 | // 740 | // [2] https://pubs.opengroup.org/onlinepubs/9799919799/functions/accept.html: "It is 741 | // implementation-defined which socket options, if any, on the accepted socket will 742 | // have a default value determined by a value previously customized by setsockopt() 743 | // on socket, rather than the default value used for other new sockets." 744 | } else { 745 | return Err(error); 746 | } 747 | } 748 | Ok(()) 749 | } 750 | 751 | struct BackingStore { 752 | fd: c_int, 753 | } 754 | 755 | impl BackingStore { 756 | pub fn new(length: usize) -> BackingStore { 757 | let count = SHM_COUNT.fetch_add(1, Ordering::Relaxed); 758 | let timestamp = UNIX_EPOCH.elapsed().unwrap(); 759 | let name = CString::new(format!( 760 | "/ipc-channel-shared-memory.{}.{}.{}.{}", 761 | count, 762 | *PID, 763 | timestamp.as_secs(), 764 | timestamp.subsec_nanos() 765 | )) 766 | .unwrap(); 767 | let fd = create_shmem(name, length); 768 | Self::from_fd(fd) 769 | } 770 | 771 | pub fn from_fd(fd: c_int) -> BackingStore { 772 | BackingStore { fd } 773 | } 774 | 775 | pub fn fd(&self) -> c_int { 776 | self.fd 777 | } 778 | 779 | pub unsafe fn map_file(&self, length: Option) -> (*mut u8, size_t) { 780 | let length = length.unwrap_or_else(|| { 781 | let mut st = mem::MaybeUninit::uninit(); 782 | if libc::fstat(self.fd, st.as_mut_ptr()) != 0 { 783 | panic!("error stating fd {}: {}", self.fd, UnixError::last()); 784 | } 785 | st.assume_init().st_size as size_t 786 | }); 787 | if length == 0 { 788 | // This will cause `mmap` to fail, so handle it explicitly. 789 | return (ptr::null_mut(), length); 790 | } 791 | let address = libc::mmap( 792 | ptr::null_mut(), 793 | length, 794 | PROT_READ | PROT_WRITE, 795 | MAP_SHARED, 796 | self.fd, 797 | 0, 798 | ); 799 | assert!(!address.is_null()); 800 | assert!(address != MAP_FAILED); 801 | (address as *mut u8, length) 802 | } 803 | } 804 | 805 | impl Drop for BackingStore { 806 | fn drop(&mut self) { 807 | unsafe { 808 | let result = libc::close(self.fd); 809 | assert!(thread::panicking() || result == 0); 810 | } 811 | } 812 | } 813 | 814 | pub struct OsIpcSharedMemory { 815 | ptr: *mut u8, 816 | length: usize, 817 | store: BackingStore, 818 | } 819 | 820 | unsafe impl Send for OsIpcSharedMemory {} 821 | unsafe impl Sync for OsIpcSharedMemory {} 822 | 823 | impl Drop for OsIpcSharedMemory { 824 | fn drop(&mut self) { 825 | unsafe { 826 | if !self.ptr.is_null() { 827 | let result = libc::munmap(self.ptr as *mut c_void, self.length); 828 | assert!(thread::panicking() || result == 0); 829 | } 830 | } 831 | } 832 | } 833 | 834 | impl Clone for OsIpcSharedMemory { 835 | fn clone(&self) -> OsIpcSharedMemory { 836 | unsafe { 837 | let store = BackingStore::from_fd(libc::dup(self.store.fd())); 838 | let (address, _) = store.map_file(Some(self.length)); 839 | OsIpcSharedMemory::from_raw_parts(address, self.length, store) 840 | } 841 | } 842 | } 843 | 844 | impl PartialEq for OsIpcSharedMemory { 845 | fn eq(&self, other: &OsIpcSharedMemory) -> bool { 846 | **self == **other 847 | } 848 | } 849 | 850 | impl Debug for OsIpcSharedMemory { 851 | fn fmt(&self, formatter: &mut Formatter) -> Result<(), fmt::Error> { 852 | (**self).fmt(formatter) 853 | } 854 | } 855 | 856 | impl Deref for OsIpcSharedMemory { 857 | type Target = [u8]; 858 | 859 | #[inline] 860 | fn deref(&self) -> &[u8] { 861 | unsafe { slice::from_raw_parts(self.ptr, self.length) } 862 | } 863 | } 864 | 865 | impl OsIpcSharedMemory { 866 | #[inline] 867 | pub unsafe fn deref_mut(&mut self) -> &mut [u8] { 868 | unsafe { slice::from_raw_parts_mut(self.ptr, self.length) } 869 | } 870 | } 871 | 872 | impl OsIpcSharedMemory { 873 | unsafe fn from_raw_parts( 874 | ptr: *mut u8, 875 | length: usize, 876 | store: BackingStore, 877 | ) -> OsIpcSharedMemory { 878 | OsIpcSharedMemory { ptr, length, store } 879 | } 880 | 881 | unsafe fn from_fd(fd: c_int) -> OsIpcSharedMemory { 882 | let store = BackingStore::from_fd(fd); 883 | let (ptr, length) = store.map_file(None); 884 | OsIpcSharedMemory::from_raw_parts(ptr, length, store) 885 | } 886 | 887 | pub fn from_byte(byte: u8, length: usize) -> OsIpcSharedMemory { 888 | unsafe { 889 | let store = BackingStore::new(length); 890 | let (address, _) = store.map_file(Some(length)); 891 | for element in slice::from_raw_parts_mut(address, length) { 892 | *element = byte; 893 | } 894 | OsIpcSharedMemory::from_raw_parts(address, length, store) 895 | } 896 | } 897 | 898 | pub fn from_bytes(bytes: &[u8]) -> OsIpcSharedMemory { 899 | unsafe { 900 | let store = BackingStore::new(bytes.len()); 901 | let (address, _) = store.map_file(Some(bytes.len())); 902 | ptr::copy_nonoverlapping(bytes.as_ptr(), address, bytes.len()); 903 | OsIpcSharedMemory::from_raw_parts(address, bytes.len(), store) 904 | } 905 | } 906 | } 907 | 908 | #[derive(Debug)] 909 | pub enum UnixError { 910 | Errno(c_int), 911 | ChannelClosed, 912 | IoError(io::Error), 913 | } 914 | 915 | impl UnixError { 916 | fn last() -> UnixError { 917 | UnixError::Errno(io::Error::last_os_error().raw_os_error().unwrap()) 918 | } 919 | 920 | #[allow(dead_code)] 921 | pub fn channel_is_closed(&self) -> bool { 922 | matches!(self, UnixError::ChannelClosed) 923 | } 924 | } 925 | 926 | impl fmt::Display for UnixError { 927 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 928 | match self { 929 | UnixError::Errno(errno) => { 930 | fmt::Display::fmt(&io::Error::from_raw_os_error(*errno), fmt) 931 | }, 932 | UnixError::ChannelClosed => write!(fmt, "All senders for this socket closed"), 933 | UnixError::IoError(e) => write!(fmt, "{e}"), 934 | } 935 | } 936 | } 937 | 938 | impl StdError for UnixError {} 939 | 940 | impl From for bincode::Error { 941 | fn from(unix_error: UnixError) -> Self { 942 | io::Error::from(unix_error).into() 943 | } 944 | } 945 | 946 | impl From for io::Error { 947 | fn from(unix_error: UnixError) -> io::Error { 948 | match unix_error { 949 | UnixError::Errno(errno) => io::Error::from_raw_os_error(errno), 950 | UnixError::ChannelClosed => io::Error::new(io::ErrorKind::ConnectionReset, unix_error), 951 | UnixError::IoError(e) => e, 952 | } 953 | } 954 | } 955 | 956 | impl From for ipc::IpcError { 957 | fn from(error: UnixError) -> Self { 958 | match error { 959 | UnixError::ChannelClosed => ipc::IpcError::Disconnected, 960 | e => ipc::IpcError::Io(io::Error::from(e)), 961 | } 962 | } 963 | } 964 | 965 | impl From for ipc::TryRecvError { 966 | fn from(error: UnixError) -> Self { 967 | match error { 968 | UnixError::ChannelClosed => ipc::TryRecvError::IpcError(ipc::IpcError::Disconnected), 969 | UnixError::Errno(code) if code == EAGAIN || code == EWOULDBLOCK => { 970 | ipc::TryRecvError::Empty 971 | }, 972 | e => ipc::TryRecvError::IpcError(ipc::IpcError::Io(io::Error::from(e))), 973 | } 974 | } 975 | } 976 | 977 | impl From for UnixError { 978 | fn from(e: io::Error) -> UnixError { 979 | if let Some(errno) = e.raw_os_error() { 980 | UnixError::Errno(errno) 981 | } else { 982 | assert!(e.kind() == io::ErrorKind::ConnectionReset); 983 | UnixError::ChannelClosed 984 | } 985 | } 986 | } 987 | 988 | #[derive(Copy, Clone)] 989 | enum BlockingMode { 990 | Blocking, 991 | Nonblocking, 992 | Timeout(Duration), 993 | } 994 | 995 | #[allow(clippy::uninit_vec, clippy::type_complexity)] 996 | fn recv(fd: c_int, blocking_mode: BlockingMode) -> Result { 997 | let (mut channels, mut shared_memory_regions) = (Vec::new(), Vec::new()); 998 | 999 | // First fragments begins with a header recording the total data length. 1000 | // 1001 | // We use this to determine whether we already got the entire message, 1002 | // or need to receive additional fragments -- and if so, how much. 1003 | let mut total_size = 0usize; 1004 | let mut main_data_buffer; 1005 | unsafe { 1006 | // Allocate a buffer without initialising the memory. 1007 | main_data_buffer = Vec::with_capacity(OsIpcSender::get_max_fragment_size()); 1008 | main_data_buffer.set_len(OsIpcSender::get_max_fragment_size()); 1009 | 1010 | let mut iovec = [ 1011 | iovec { 1012 | iov_base: &mut total_size as *mut _ as *mut c_void, 1013 | iov_len: mem::size_of_val(&total_size), 1014 | }, 1015 | iovec { 1016 | iov_base: main_data_buffer.as_mut_ptr() as *mut c_void, 1017 | iov_len: main_data_buffer.len(), 1018 | }, 1019 | ]; 1020 | let mut cmsg = UnixCmsg::new(&mut iovec)?; 1021 | 1022 | let bytes_read = cmsg.recv(fd, blocking_mode)?; 1023 | main_data_buffer.set_len(bytes_read - mem::size_of_val(&total_size)); 1024 | 1025 | let cmsg_fds = CMSG_DATA(cmsg.cmsg_buffer) as *const c_int; 1026 | let cmsg_length = cmsg.msghdr.msg_controllen; 1027 | let channel_length = if cmsg_length == 0 { 1028 | 0 1029 | } else { 1030 | // The control header is followed by an array of FDs. The size of the control header is 1031 | // determined by CMSG_SPACE. (On Linux this would the same as CMSG_ALIGN, but that isn't 1032 | // exposed by libc. CMSG_SPACE(0) is the portable version of that.) 1033 | (cmsg.cmsg_len() - CMSG_SPACE(0) as size_t) / mem::size_of::() 1034 | }; 1035 | for index in 0..channel_length { 1036 | let fd = *cmsg_fds.add(index); 1037 | if is_socket(fd) { 1038 | channels.push(OsOpaqueIpcChannel::from_fd(fd)); 1039 | continue; 1040 | } 1041 | shared_memory_regions.push(OsIpcSharedMemory::from_fd(fd)); 1042 | } 1043 | } 1044 | 1045 | if total_size == main_data_buffer.len() { 1046 | // Fast path: no fragments. 1047 | return Ok(IpcMessage::new( 1048 | main_data_buffer, 1049 | channels, 1050 | shared_memory_regions, 1051 | )); 1052 | } 1053 | 1054 | // Reassemble fragments. 1055 | // 1056 | // The initial fragment carries the receive end of a dedicated channel 1057 | // through which all the remaining fragments will be coming in. 1058 | let dedicated_rx = channels.pop().unwrap().to_receiver(); 1059 | 1060 | // Extend the buffer to hold the entire message, without initialising the memory. 1061 | let len = main_data_buffer.len(); 1062 | main_data_buffer.reserve_exact(total_size - len); 1063 | 1064 | // Receive followup fragments directly into the main buffer. 1065 | while main_data_buffer.len() < total_size { 1066 | let write_pos = main_data_buffer.len(); 1067 | let end_pos = cmp::min( 1068 | write_pos + OsIpcSender::fragment_size(*SYSTEM_SENDBUF_SIZE), 1069 | total_size, 1070 | ); 1071 | let result = unsafe { 1072 | assert!(end_pos <= main_data_buffer.capacity()); 1073 | main_data_buffer.set_len(end_pos); 1074 | 1075 | // Integer underflow could make the following code unsound... 1076 | assert!(end_pos >= write_pos); 1077 | 1078 | // Note: we always use blocking mode for followup fragments, 1079 | // to make sure that once we start receiving a multi-fragment message, 1080 | // we don't abort in the middle of it... 1081 | let result = libc::recv( 1082 | dedicated_rx.fd.get(), 1083 | main_data_buffer[write_pos..].as_mut_ptr() as *mut c_void, 1084 | end_pos - write_pos, 1085 | 0, 1086 | ); 1087 | main_data_buffer.set_len(write_pos + cmp::max(result, 0) as usize); 1088 | result 1089 | }; 1090 | 1091 | match result.cmp(&0) { 1092 | cmp::Ordering::Greater => continue, 1093 | cmp::Ordering::Equal => return Err(UnixError::ChannelClosed), 1094 | cmp::Ordering::Less => return Err(UnixError::last()), 1095 | } 1096 | } 1097 | 1098 | Ok(IpcMessage::new( 1099 | main_data_buffer, 1100 | channels, 1101 | shared_memory_regions, 1102 | )) 1103 | } 1104 | 1105 | // https://github.com/servo/ipc-channel/issues/192 1106 | fn new_msghdr(iovec: &mut [iovec], cmsg_buffer: *mut cmsghdr, cmsg_space: MsgControlLen) -> msghdr { 1107 | let mut msghdr: msghdr = unsafe { mem::zeroed() }; 1108 | msghdr.msg_name = ptr::null_mut(); 1109 | msghdr.msg_namelen = 0; 1110 | msghdr.msg_iov = iovec.as_mut_ptr(); 1111 | msghdr.msg_iovlen = iovec.len() as IovLen; 1112 | msghdr.msg_control = cmsg_buffer as *mut c_void; 1113 | msghdr.msg_controllen = cmsg_space; 1114 | msghdr.msg_flags = 0; 1115 | msghdr 1116 | } 1117 | 1118 | fn create_shmem(name: CString, length: usize) -> c_int { 1119 | unsafe { 1120 | let fd = libc::memfd_create(name.as_ptr(), libc::MFD_CLOEXEC); 1121 | assert!(fd >= 0); 1122 | assert_eq!(libc::ftruncate(fd, length as off_t), 0); 1123 | fd 1124 | } 1125 | } 1126 | 1127 | struct UnixCmsg { 1128 | cmsg_buffer: *mut cmsghdr, 1129 | msghdr: msghdr, 1130 | } 1131 | 1132 | unsafe impl Send for UnixCmsg {} 1133 | 1134 | impl Drop for UnixCmsg { 1135 | fn drop(&mut self) { 1136 | unsafe { 1137 | libc::free(self.cmsg_buffer as *mut c_void); 1138 | } 1139 | } 1140 | } 1141 | 1142 | impl UnixCmsg { 1143 | unsafe fn new(iovec: &mut [iovec]) -> Result { 1144 | let cmsg_length = CMSG_SPACE(MAX_FDS_IN_CMSG * (mem::size_of::() as c_uint)); 1145 | let cmsg_buffer = libc::malloc(cmsg_length as usize) as *mut cmsghdr; 1146 | if cmsg_buffer.is_null() { 1147 | return Err(UnixError::last()); 1148 | } 1149 | Ok(UnixCmsg { 1150 | cmsg_buffer, 1151 | msghdr: new_msghdr(iovec, cmsg_buffer, cmsg_length as MsgControlLen), 1152 | }) 1153 | } 1154 | 1155 | unsafe fn recv(&mut self, fd: c_int, blocking_mode: BlockingMode) -> Result { 1156 | match blocking_mode { 1157 | BlockingMode::Nonblocking => { 1158 | if libc::fcntl(fd, libc::F_SETFL, libc::O_NONBLOCK) < 0 { 1159 | return Err(UnixError::last()); 1160 | } 1161 | }, 1162 | BlockingMode::Timeout(duration) => { 1163 | let events = libc::POLLIN | libc::POLLPRI | libc::POLLRDHUP; 1164 | 1165 | let mut fd = [libc::pollfd { 1166 | fd, 1167 | events, 1168 | revents: 0, 1169 | }]; 1170 | let result = libc::poll( 1171 | fd.as_mut_ptr(), 1172 | fd.len() as _, 1173 | duration.as_millis().try_into().unwrap_or(-1), 1174 | ); 1175 | 1176 | match result.cmp(&0) { 1177 | cmp::Ordering::Equal => return Err(UnixError::Errno(EAGAIN)), 1178 | cmp::Ordering::Less => return Err(UnixError::last()), 1179 | cmp::Ordering::Greater => {}, 1180 | } 1181 | }, 1182 | BlockingMode::Blocking => {}, 1183 | } 1184 | 1185 | let result = recvmsg(fd, &mut self.msghdr, RECVMSG_FLAGS); 1186 | 1187 | let result = match result.cmp(&0) { 1188 | cmp::Ordering::Equal => Err(UnixError::ChannelClosed), 1189 | cmp::Ordering::Less => Err(UnixError::last()), 1190 | cmp::Ordering::Greater => Ok(result as usize), 1191 | }; 1192 | 1193 | if let BlockingMode::Nonblocking = blocking_mode { 1194 | if libc::fcntl(fd, libc::F_SETFL, 0) < 0 { 1195 | return Err(UnixError::last()); 1196 | } 1197 | } 1198 | result 1199 | } 1200 | 1201 | unsafe fn cmsg_len(&self) -> size_t { 1202 | (*(self.msghdr.msg_control as *const cmsghdr)).cmsg_len as size_t 1203 | } 1204 | } 1205 | 1206 | fn is_socket(fd: c_int) -> bool { 1207 | unsafe { 1208 | let mut st = mem::MaybeUninit::uninit(); 1209 | if libc::fstat(fd, st.as_mut_ptr()) != 0 { 1210 | return false; 1211 | } 1212 | (st.assume_init().st_mode & S_IFMT) == S_IFSOCK 1213 | } 1214 | } 1215 | -------------------------------------------------------------------------------- /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; 19 | 20 | use crate::ipc::OpaqueIpcReceiver; 21 | use crate::ipc::{self, IpcMessage, IpcReceiver, IpcReceiverSet, IpcSelectionResult, IpcSender}; 22 | use crossbeam_channel::{self, Receiver, Sender}; 23 | use serde::{Deserialize, Serialize}; 24 | 25 | /// Global object wrapping a `RouterProxy`. 26 | /// Add routes ([add_route](RouterProxy::add_route)), or convert IpcReceiver 27 | /// to crossbeam channels (e.g. [route_ipc_receiver_to_new_crossbeam_receiver](RouterProxy::route_ipc_receiver_to_new_crossbeam_receiver)) 28 | pub static ROUTER: LazyLock = LazyLock::new(RouterProxy::new); 29 | 30 | /// A `RouterProxy` provides methods for talking to the router. Calling 31 | /// [new](RouterProxy::new) automatically spins up a router thread which 32 | /// waits for events on its registered `IpcReceiver`s. The `RouterProxy`'s 33 | /// methods communicate with the running router thread to register new 34 | /// `IpcReceiver`'s 35 | pub struct RouterProxy { 36 | comm: Mutex, 37 | } 38 | 39 | #[allow(clippy::new_without_default)] 40 | impl RouterProxy { 41 | pub fn new() -> RouterProxy { 42 | // Router acts like a receiver, running in its own thread with both 43 | // receiver ends. 44 | // Router proxy takes both sending ends. 45 | let (msg_sender, msg_receiver) = crossbeam_channel::unbounded(); 46 | let (wakeup_sender, wakeup_receiver) = ipc::channel().unwrap(); 47 | thread::Builder::new() 48 | .name("router-proxy".to_string()) 49 | .spawn(move || Router::new(msg_receiver, wakeup_receiver).run()) 50 | .expect("Failed to spawn router proxy thread"); 51 | RouterProxy { 52 | comm: Mutex::new(RouterProxyComm { 53 | msg_sender, 54 | wakeup_sender, 55 | shutdown: false, 56 | }), 57 | } 58 | } 59 | 60 | /// Add a new (receiver, callback) pair to the router, and send a wakeup message 61 | /// to the router. 62 | /// 63 | /// Consider using [add_typed_route](Self::add_typed_route) instead, which prevents 64 | /// mismatches between the receiver and callback types. 65 | #[deprecated(since = "0.19.0", note = "please use 'add_typed_route' instead")] 66 | pub fn add_route(&self, receiver: OpaqueIpcReceiver, callback: RouterHandler) { 67 | let comm = self.comm.lock().unwrap(); 68 | 69 | if comm.shutdown { 70 | return; 71 | } 72 | 73 | comm.msg_sender 74 | .send(RouterMsg::AddRoute(receiver, callback)) 75 | .unwrap(); 76 | comm.wakeup_sender.send(()).unwrap(); 77 | } 78 | 79 | /// Add a new `(receiver, callback)` pair to the router, and send a wakeup message 80 | /// to the router. 81 | /// 82 | /// Unlike [add_route](Self::add_route) this method is strongly typed and guarantees 83 | /// that the `receiver` and the `callback` use the same message type. 84 | pub fn add_typed_route(&self, receiver: IpcReceiver, mut callback: TypedRouterHandler) 85 | where 86 | T: Serialize + for<'de> Deserialize<'de> + 'static, 87 | { 88 | // Before passing the message on to the callback, turn it into the appropriate type 89 | let modified_callback = move |msg: IpcMessage| { 90 | let typed_message = msg.to::(); 91 | callback(typed_message) 92 | }; 93 | 94 | #[allow(deprecated)] 95 | self.add_route(receiver.to_opaque(), Box::new(modified_callback)); 96 | } 97 | 98 | /// Send a shutdown message to the router containing a ACK sender, 99 | /// send a wakeup message to the router, and block on the ACK. 100 | /// Calling it is idempotent, 101 | /// which can be useful when running a multi-process system in single-process mode. 102 | pub fn shutdown(&self) { 103 | let mut comm = self.comm.lock().unwrap(); 104 | 105 | if comm.shutdown { 106 | return; 107 | } 108 | comm.shutdown = true; 109 | 110 | let (ack_sender, ack_receiver) = crossbeam_channel::unbounded(); 111 | comm.wakeup_sender 112 | .send(()) 113 | .map(|_| { 114 | comm.msg_sender 115 | .send(RouterMsg::Shutdown(ack_sender)) 116 | .unwrap(); 117 | ack_receiver.recv().unwrap(); 118 | }) 119 | .unwrap(); 120 | } 121 | 122 | /// A convenience function to route an `IpcReceiver` to an existing `Sender`. 123 | pub fn route_ipc_receiver_to_crossbeam_sender( 124 | &self, 125 | ipc_receiver: IpcReceiver, 126 | crossbeam_sender: Sender, 127 | ) where 128 | T: for<'de> Deserialize<'de> + Serialize + Send + 'static, 129 | { 130 | self.add_typed_route( 131 | ipc_receiver, 132 | Box::new(move |message| drop(crossbeam_sender.send(message.unwrap()))), 133 | ) 134 | } 135 | 136 | /// A convenience function to route an `IpcReceiver` to a `Receiver`: the most common 137 | /// use of a `Router`. 138 | pub fn route_ipc_receiver_to_new_crossbeam_receiver( 139 | &self, 140 | ipc_receiver: IpcReceiver, 141 | ) -> Receiver 142 | where 143 | T: for<'de> Deserialize<'de> + Serialize + Send + 'static, 144 | { 145 | let (crossbeam_sender, crossbeam_receiver) = crossbeam_channel::unbounded(); 146 | self.route_ipc_receiver_to_crossbeam_sender(ipc_receiver, crossbeam_sender); 147 | crossbeam_receiver 148 | } 149 | } 150 | 151 | struct RouterProxyComm { 152 | msg_sender: Sender, 153 | wakeup_sender: IpcSender<()>, 154 | shutdown: bool, 155 | } 156 | 157 | /// Router runs in its own thread listening for events. Adds events to its IpcReceiverSet 158 | /// and listens for events using select(). 159 | struct Router { 160 | /// Get messages from RouterProxy. 161 | msg_receiver: Receiver, 162 | /// The ID/index of the special channel we use to identify messages from msg_receiver. 163 | msg_wakeup_id: u64, 164 | /// Set of all receivers which have been registered for us to select on. 165 | ipc_receiver_set: IpcReceiverSet, 166 | /// Maps ids to their handler functions. 167 | handlers: HashMap, 168 | } 169 | 170 | impl Router { 171 | fn new(msg_receiver: Receiver, wakeup_receiver: IpcReceiver<()>) -> Router { 172 | let mut ipc_receiver_set = IpcReceiverSet::new().unwrap(); 173 | let msg_wakeup_id = ipc_receiver_set.add(wakeup_receiver).unwrap(); 174 | Router { 175 | msg_receiver, 176 | msg_wakeup_id, 177 | ipc_receiver_set, 178 | handlers: HashMap::new(), 179 | } 180 | } 181 | 182 | /// Continuously loop waiting for wakeup signals from router proxy. 183 | /// Iterate over events either: 184 | /// 1) If a message comes in from our special `wakeup_receiver` (identified through 185 | /// msg_wakeup_id. Read message from `msg_receiver` and add a new receiver 186 | /// to our receiver set. 187 | /// 2) Call appropriate handler based on message id. 188 | /// 3) Remove handler once channel closes. 189 | fn run(&mut self) { 190 | loop { 191 | // Wait for events to come from our select() new channels are added to 192 | // our ReceiverSet below. 193 | let results = match self.ipc_receiver_set.select() { 194 | Ok(results) => results, 195 | Err(_) => break, 196 | }; 197 | 198 | // Iterate over numerous events that were ready at this time. 199 | for result in results.into_iter() { 200 | match result { 201 | // Message came from the RouterProxy. Listen on our `msg_receiver` 202 | // channel. 203 | IpcSelectionResult::MessageReceived(id, _) if id == self.msg_wakeup_id => { 204 | match self.msg_receiver.recv().unwrap() { 205 | RouterMsg::AddRoute(receiver, handler) => { 206 | let new_receiver_id = 207 | self.ipc_receiver_set.add_opaque(receiver).unwrap(); 208 | self.handlers.insert(new_receiver_id, handler); 209 | }, 210 | RouterMsg::Shutdown(sender) => { 211 | sender 212 | .send(()) 213 | .expect("Failed to send comfirmation of shutdown."); 214 | break; 215 | }, 216 | } 217 | }, 218 | // Event from one of our registered receivers, call callback. 219 | IpcSelectionResult::MessageReceived(id, message) => { 220 | self.handlers.get_mut(&id).unwrap()(message) 221 | }, 222 | IpcSelectionResult::ChannelClosed(id) => { 223 | let _ = self.handlers.remove(&id).unwrap(); 224 | }, 225 | } 226 | } 227 | } 228 | } 229 | } 230 | 231 | enum RouterMsg { 232 | /// Register the receiver OpaqueIpcReceiver for listening for events on. 233 | /// When a message comes from this receiver, call RouterHandler. 234 | AddRoute(OpaqueIpcReceiver, RouterHandler), 235 | /// Shutdown the router, providing a sender to send an acknowledgement. 236 | Shutdown(Sender<()>), 237 | } 238 | 239 | /// Function to call when a new event is received from the corresponding receiver. 240 | pub type RouterHandler = Box; 241 | 242 | /// Like [RouterHandler] but includes the type that will be passed to the callback 243 | pub type TypedRouterHandler = Box) + Send>; 244 | -------------------------------------------------------------------------------- /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 | #[cfg(not(any(feature = "force-inprocess", target_os = "android", target_os = "ios")))] 11 | use crate::ipc::IpcReceiver; 12 | use crate::ipc::{self, IpcReceiverSet, IpcSender, IpcSharedMemory}; 13 | use crate::router::{RouterProxy, ROUTER}; 14 | use crossbeam_channel::{self, Sender}; 15 | use serde::{Deserialize, Deserializer, Serialize, Serializer}; 16 | use std::cell::RefCell; 17 | #[cfg(not(any(feature = "force-inprocess", target_os = "android", target_os = "ios")))] 18 | use std::env; 19 | use std::iter; 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::thread; 31 | 32 | #[cfg(not(any( 33 | feature = "force-inprocess", 34 | target_os = "android", 35 | target_os = "ios", 36 | target_os = "windows" 37 | )))] 38 | use crate::ipc::IpcOneShotServer; 39 | 40 | #[cfg(not(any( 41 | feature = "force-inprocess", 42 | target_os = "android", 43 | target_os = "ios", 44 | target_os = "windows", 45 | )))] 46 | use std::io::Error; 47 | use std::time::{Duration, Instant}; 48 | 49 | #[cfg(not(any( 50 | feature = "force-inprocess", 51 | target_os = "windows", 52 | target_os = "android", 53 | target_os = "ios" 54 | )))] 55 | // I'm not actually sure invoking this is indeed unsafe -- but better safe than sorry... 56 | pub unsafe fn fork(child_func: F) -> libc::pid_t { 57 | match libc::fork() { 58 | -1 => panic!("Fork failed: {}", Error::last_os_error()), 59 | 0 => { 60 | child_func(); 61 | libc::exit(0); 62 | }, 63 | pid => pid, 64 | } 65 | } 66 | 67 | #[cfg(not(any( 68 | feature = "force-inprocess", 69 | target_os = "windows", 70 | target_os = "android", 71 | target_os = "ios" 72 | )))] 73 | pub trait Wait { 74 | fn wait(self); 75 | } 76 | 77 | #[cfg(not(any( 78 | feature = "force-inprocess", 79 | target_os = "windows", 80 | target_os = "android", 81 | target_os = "ios" 82 | )))] 83 | impl Wait for libc::pid_t { 84 | fn wait(self) { 85 | unsafe { 86 | libc::waitpid(self, ptr::null_mut(), 0); 87 | } 88 | } 89 | } 90 | 91 | // Helper to get a channel_name argument passed in; used for the 92 | // cross-process spawn server tests. 93 | #[cfg(not(any(feature = "force-inprocess", target_os = "android", target_os = "ios")))] 94 | pub fn get_channel_name_arg(which: &str) -> Option { 95 | for arg in env::args() { 96 | let arg_str = &*format!("channel_name-{}:", which); 97 | if let Some(arg) = arg.strip_prefix(arg_str) { 98 | return Some(arg.to_owned()); 99 | } 100 | } 101 | None 102 | } 103 | 104 | // Helper to get a channel_name argument passed in; used for the 105 | // cross-process spawn server tests. 106 | #[cfg(not(any(feature = "force-inprocess", target_os = "android", target_os = "ios",)))] 107 | pub fn spawn_server(test_name: &str, server_args: &[(&str, &str)]) -> process::Child { 108 | Command::new(env::current_exe().unwrap()) 109 | .arg(test_name) 110 | .args( 111 | server_args 112 | .iter() 113 | .map(|(name, val)| format!("channel_name-{}:{}", name, val)), 114 | ) 115 | .stdin(Stdio::null()) 116 | .stdout(Stdio::null()) 117 | .stderr(Stdio::null()) 118 | .spawn() 119 | .expect("failed to execute server process") 120 | } 121 | 122 | type Person = (String, u32); 123 | 124 | #[test] 125 | fn simple() { 126 | let person = ("Patrick Walton".to_owned(), 29); 127 | let (tx, rx) = ipc::channel().unwrap(); 128 | tx.send(person.clone()).unwrap(); 129 | let received_person = rx.recv().unwrap(); 130 | assert_eq!(person, received_person); 131 | drop(tx); 132 | match rx.recv().unwrap_err() { 133 | ipc::IpcError::Disconnected => (), 134 | e => panic!("expected disconnected error, got {:?}", e), 135 | } 136 | } 137 | 138 | #[test] 139 | fn embedded_senders() { 140 | let person = ("Patrick Walton".to_owned(), 29); 141 | let (sub_tx, sub_rx) = ipc::channel().unwrap(); 142 | let person_and_sender = (person.clone(), sub_tx); 143 | let (super_tx, super_rx) = ipc::channel().unwrap(); 144 | super_tx.send(person_and_sender).unwrap(); 145 | let received_person_and_sender = super_rx.recv().unwrap(); 146 | assert_eq!(received_person_and_sender.0, person); 147 | received_person_and_sender.1.send(person.clone()).unwrap(); 148 | let received_person = sub_rx.recv().unwrap(); 149 | assert_eq!(received_person, person); 150 | } 151 | 152 | #[test] 153 | fn embedded_receivers() { 154 | let person = ("Patrick Walton".to_owned(), 29); 155 | let (sub_tx, sub_rx) = ipc::channel().unwrap(); 156 | let person_and_receiver = (person.clone(), sub_rx); 157 | let (super_tx, super_rx) = ipc::channel().unwrap(); 158 | super_tx.send(person_and_receiver).unwrap(); 159 | let received_person_and_receiver = super_rx.recv().unwrap(); 160 | assert_eq!(received_person_and_receiver.0, person); 161 | sub_tx.send(person.clone()).unwrap(); 162 | let received_person = received_person_and_receiver.1.recv().unwrap(); 163 | assert_eq!(received_person, person); 164 | } 165 | 166 | #[test] 167 | fn select() { 168 | let (tx0, rx0) = ipc::channel().unwrap(); 169 | let (tx1, rx1) = ipc::channel().unwrap(); 170 | let mut rx_set = IpcReceiverSet::new().unwrap(); 171 | let rx0_id = rx_set.add(rx0).unwrap(); 172 | let rx1_id = rx_set.add(rx1).unwrap(); 173 | 174 | let person = ("Patrick Walton".to_owned(), 29); 175 | tx0.send(person.clone()).unwrap(); 176 | let (received_id, received_data) = rx_set 177 | .select() 178 | .unwrap() 179 | .into_iter() 180 | .next() 181 | .unwrap() 182 | .unwrap(); 183 | let received_person: Person = received_data.to().unwrap(); 184 | assert_eq!(received_id, rx0_id); 185 | assert_eq!(received_person, person); 186 | 187 | tx1.send(person.clone()).unwrap(); 188 | let (received_id, received_data) = rx_set 189 | .select() 190 | .unwrap() 191 | .into_iter() 192 | .next() 193 | .unwrap() 194 | .unwrap(); 195 | let received_person: Person = received_data.to().unwrap(); 196 | assert_eq!(received_id, rx1_id); 197 | assert_eq!(received_person, person); 198 | 199 | tx0.send(person.clone()).unwrap(); 200 | tx1.send(person.clone()).unwrap(); 201 | let (mut received0, mut received1) = (false, false); 202 | while !received0 || !received1 { 203 | for result in rx_set.select().unwrap().into_iter() { 204 | let (received_id, received_data) = result.unwrap(); 205 | let received_person: Person = received_data.to().unwrap(); 206 | assert_eq!(received_person, person); 207 | assert!(received_id == rx0_id || received_id == rx1_id); 208 | if received_id == rx0_id { 209 | assert!(!received0); 210 | received0 = true; 211 | } else if received_id == rx1_id { 212 | assert!(!received1); 213 | received1 = true; 214 | } 215 | } 216 | } 217 | } 218 | 219 | #[cfg(not(any(feature = "force-inprocess", target_os = "android", target_os = "ios")))] 220 | #[test] 221 | fn cross_process_embedded_senders_spawn() { 222 | let person = ("Patrick Walton".to_owned(), 29); 223 | 224 | let server0_name = get_channel_name_arg("server0"); 225 | let server2_name = get_channel_name_arg("server2"); 226 | if let (Some(server0_name), Some(server2_name)) = (server0_name, server2_name) { 227 | let (tx1, rx1): (IpcSender, IpcReceiver) = ipc::channel().unwrap(); 228 | let tx0 = IpcSender::connect(server0_name).unwrap(); 229 | tx0.send(tx1).unwrap(); 230 | rx1.recv().unwrap(); 231 | let tx2: IpcSender = IpcSender::connect(server2_name).unwrap(); 232 | tx2.send(person.clone()).unwrap(); 233 | 234 | unsafe { 235 | libc::exit(0); 236 | } 237 | } 238 | } 239 | 240 | #[cfg(not(any( 241 | feature = "force-inprocess", 242 | target_os = "windows", 243 | target_os = "android", 244 | target_os = "ios" 245 | )))] 246 | #[test] 247 | fn cross_process_embedded_senders_fork() { 248 | let person = ("Patrick Walton".to_owned(), 29); 249 | let (server0, server0_name) = IpcOneShotServer::new().unwrap(); 250 | let (server2, server2_name) = IpcOneShotServer::new().unwrap(); 251 | let child_pid = unsafe { 252 | fork(|| { 253 | let (tx1, rx1): (IpcSender, IpcReceiver) = ipc::channel().unwrap(); 254 | let tx0 = IpcSender::connect(server0_name).unwrap(); 255 | tx0.send(tx1).unwrap(); 256 | rx1.recv().unwrap(); 257 | let tx2: IpcSender = IpcSender::connect(server2_name).unwrap(); 258 | tx2.send(person.clone()).unwrap(); 259 | }) 260 | }; 261 | let (_, tx1): (_, IpcSender) = server0.accept().unwrap(); 262 | tx1.send(person.clone()).unwrap(); 263 | let (_, received_person): (_, Person) = server2.accept().unwrap(); 264 | child_pid.wait(); 265 | assert_eq!(received_person, person); 266 | } 267 | 268 | #[test] 269 | fn router_simple_global() { 270 | // Note: All ROUTER operation need to run in a single test, 271 | // since the state of the router will carry across tests. 272 | 273 | let person = ("Patrick Walton".to_owned(), 29); 274 | let (tx, rx) = ipc::channel().unwrap(); 275 | tx.send(person.clone()).unwrap(); 276 | 277 | let (callback_fired_sender, callback_fired_receiver) = crossbeam_channel::unbounded::(); 278 | #[allow(deprecated)] 279 | ROUTER.add_route( 280 | rx.to_opaque(), 281 | Box::new(move |person| { 282 | callback_fired_sender.send(person.to().unwrap()).unwrap(); 283 | }), 284 | ); 285 | let received_person = callback_fired_receiver.recv().unwrap(); 286 | assert_eq!(received_person, person); 287 | 288 | // Try the same, with a strongly typed route 289 | let message: usize = 42; 290 | let (tx, rx) = ipc::channel().unwrap(); 291 | tx.send(message.clone()).unwrap(); 292 | 293 | let (callback_fired_sender, callback_fired_receiver) = crossbeam_channel::unbounded::(); 294 | ROUTER.add_typed_route( 295 | rx, 296 | Box::new(move |message| { 297 | callback_fired_sender.send(message.unwrap()).unwrap(); 298 | }), 299 | ); 300 | let received_message = callback_fired_receiver.recv().unwrap(); 301 | assert_eq!(received_message, message); 302 | 303 | // Now shutdown the router. 304 | ROUTER.shutdown(); 305 | 306 | // Use router after shutdown. 307 | let person = ("Patrick Walton".to_owned(), 29); 308 | let (tx, rx) = ipc::channel().unwrap(); 309 | tx.send(person.clone()).unwrap(); 310 | 311 | let (callback_fired_sender, callback_fired_receiver) = crossbeam_channel::unbounded::(); 312 | ROUTER.add_typed_route( 313 | rx, 314 | Box::new(move |person| { 315 | callback_fired_sender.send(person.unwrap()).unwrap(); 316 | }), 317 | ); 318 | 319 | // The sender should have been dropped. 320 | let received_person = callback_fired_receiver.recv(); 321 | assert!(received_person.is_err()); 322 | 323 | // Shutdown the router, again(should be a no-op). 324 | ROUTER.shutdown(); 325 | } 326 | 327 | #[test] 328 | fn router_routing_to_new_crossbeam_receiver() { 329 | let person = ("Patrick Walton".to_owned(), 29); 330 | let (tx, rx) = ipc::channel().unwrap(); 331 | tx.send(person.clone()).unwrap(); 332 | 333 | let router = RouterProxy::new(); 334 | let crossbeam_receiver = router.route_ipc_receiver_to_new_crossbeam_receiver(rx); 335 | let received_person = crossbeam_receiver.recv().unwrap(); 336 | assert_eq!(received_person, person); 337 | } 338 | 339 | #[test] 340 | fn router_multiplexing() { 341 | let person = ("Patrick Walton".to_owned(), 29); 342 | let (tx0, rx0) = ipc::channel().unwrap(); 343 | tx0.send(person.clone()).unwrap(); 344 | let (tx1, rx1) = ipc::channel().unwrap(); 345 | tx1.send(person.clone()).unwrap(); 346 | 347 | let router = RouterProxy::new(); 348 | let crossbeam_rx_0 = router.route_ipc_receiver_to_new_crossbeam_receiver(rx0); 349 | let crossbeam_rx_1 = router.route_ipc_receiver_to_new_crossbeam_receiver(rx1); 350 | let received_person_0 = crossbeam_rx_0.recv().unwrap(); 351 | let received_person_1 = crossbeam_rx_1.recv().unwrap(); 352 | assert_eq!(received_person_0, person); 353 | assert_eq!(received_person_1, person); 354 | } 355 | 356 | #[test] 357 | fn router_multithreaded_multiplexing() { 358 | let person = ("Patrick Walton".to_owned(), 29); 359 | 360 | let person_for_thread = person.clone(); 361 | let (tx0, rx0) = ipc::channel().unwrap(); 362 | thread::spawn(move || tx0.send(person_for_thread).unwrap()); 363 | let person_for_thread = person.clone(); 364 | let (tx1, rx1) = ipc::channel().unwrap(); 365 | thread::spawn(move || tx1.send(person_for_thread).unwrap()); 366 | 367 | let router = RouterProxy::new(); 368 | let crossbeam_rx_0 = router.route_ipc_receiver_to_new_crossbeam_receiver(rx0); 369 | let crossbeam_rx_1 = router.route_ipc_receiver_to_new_crossbeam_receiver(rx1); 370 | let received_person_0 = crossbeam_rx_0.recv().unwrap(); 371 | let received_person_1 = crossbeam_rx_1.recv().unwrap(); 372 | assert_eq!(received_person_0, person); 373 | assert_eq!(received_person_1, person); 374 | } 375 | 376 | #[test] 377 | fn router_drops_callbacks_on_sender_shutdown() { 378 | struct Dropper { 379 | sender: Sender, 380 | } 381 | 382 | impl Drop for Dropper { 383 | fn drop(&mut self) { 384 | self.sender.send(42).unwrap(); 385 | } 386 | } 387 | 388 | let (tx0, rx0) = ipc::channel::<()>().unwrap(); 389 | let (drop_tx, drop_rx) = crossbeam_channel::unbounded(); 390 | let dropper = Dropper { sender: drop_tx }; 391 | 392 | let router = RouterProxy::new(); 393 | router.add_typed_route( 394 | rx0, 395 | Box::new(move |_| { 396 | let _ = &dropper; 397 | }), 398 | ); 399 | drop(tx0); 400 | assert_eq!(drop_rx.recv(), Ok(42)); 401 | } 402 | 403 | #[test] 404 | fn router_drops_callbacks_on_cloned_sender_shutdown() { 405 | struct Dropper { 406 | sender: Sender, 407 | } 408 | 409 | impl Drop for Dropper { 410 | fn drop(&mut self) { 411 | self.sender.send(42).unwrap() 412 | } 413 | } 414 | 415 | let (tx0, rx0) = ipc::channel::<()>().unwrap(); 416 | let (drop_tx, drop_rx) = crossbeam_channel::unbounded(); 417 | let dropper = Dropper { sender: drop_tx }; 418 | 419 | let router = RouterProxy::new(); 420 | router.add_typed_route( 421 | rx0, 422 | Box::new(move |_| { 423 | let _ = &dropper; 424 | }), 425 | ); 426 | let txs = vec![tx0.clone(), tx0.clone(), tx0.clone()]; 427 | drop(txs); 428 | drop(tx0); 429 | assert_eq!(drop_rx.recv(), Ok(42)); 430 | } 431 | 432 | #[test] 433 | fn router_big_data() { 434 | let person = ("Patrick Walton".to_owned(), 29); 435 | let people: Vec<_> = iter::repeat(person).take(64 * 1024).collect(); 436 | let (tx, rx) = ipc::channel().unwrap(); 437 | let people_for_subthread = people.clone(); 438 | let thread = thread::spawn(move || { 439 | tx.send(people_for_subthread).unwrap(); 440 | }); 441 | 442 | let (callback_fired_sender, callback_fired_receiver) = 443 | crossbeam_channel::unbounded::>(); 444 | let router = RouterProxy::new(); 445 | router.add_typed_route( 446 | rx, 447 | Box::new(move |people| callback_fired_sender.send(people.unwrap()).unwrap()), 448 | ); 449 | let received_people = callback_fired_receiver.recv().unwrap(); 450 | assert_eq!(received_people, people); 451 | thread.join().unwrap(); 452 | } 453 | 454 | #[test] 455 | fn shared_memory() { 456 | let person = ("Patrick Walton".to_owned(), 29); 457 | let person_and_shared_memory = (person, IpcSharedMemory::from_byte(0xba, 1024 * 1024)); 458 | let (tx, rx) = ipc::channel().unwrap(); 459 | tx.send(person_and_shared_memory.clone()).unwrap(); 460 | let received_person_and_shared_memory = rx.recv().unwrap(); 461 | assert_eq!( 462 | received_person_and_shared_memory.0, 463 | person_and_shared_memory.0 464 | ); 465 | assert!(person_and_shared_memory.1.iter().all(|byte| *byte == 0xba)); 466 | assert!(received_person_and_shared_memory 467 | .1 468 | .iter() 469 | .all(|byte| *byte == 0xba)); 470 | } 471 | 472 | #[test] 473 | fn shared_memory_slice() { 474 | let (tx, rx) = ipc::channel().unwrap(); 475 | // test byte of size 0 476 | let shared_memory = IpcSharedMemory::from_byte(42, 0); 477 | tx.send(shared_memory.clone()).unwrap(); 478 | let received_shared_memory = rx.recv().unwrap(); 479 | assert_eq!(*received_shared_memory, *shared_memory); 480 | // test empty slice 481 | let shared_memory = IpcSharedMemory::from_bytes(&[]); 482 | tx.send(shared_memory.clone()).unwrap(); 483 | let received_shared_memory = rx.recv().unwrap(); 484 | assert_eq!(*received_shared_memory, *shared_memory); 485 | // test non-empty slice 486 | let shared_memory = IpcSharedMemory::from_bytes(&[4, 2, 42]); 487 | tx.send(shared_memory.clone()).unwrap(); 488 | let received_shared_memory = rx.recv().unwrap(); 489 | assert_eq!(*received_shared_memory, *shared_memory); 490 | } 491 | 492 | #[test] 493 | fn shared_memory_object_equality() { 494 | let person = ("Patrick Walton".to_owned(), 29); 495 | let person_and_shared_memory = (person, IpcSharedMemory::from_byte(0xba, 1024 * 1024)); 496 | let (tx, rx) = ipc::channel().unwrap(); 497 | tx.send(person_and_shared_memory.clone()).unwrap(); 498 | let received_person_and_shared_memory = rx.recv().unwrap(); 499 | assert_eq!(received_person_and_shared_memory, person_and_shared_memory); 500 | } 501 | 502 | #[test] 503 | fn opaque_sender() { 504 | let person = ("Patrick Walton".to_owned(), 29); 505 | let (tx, rx) = ipc::channel().unwrap(); 506 | let opaque_tx = tx.to_opaque(); 507 | let tx: IpcSender = opaque_tx.to(); 508 | tx.send(person.clone()).unwrap(); 509 | let received_person = rx.recv().unwrap(); 510 | assert_eq!(person, received_person); 511 | } 512 | 513 | #[test] 514 | fn embedded_opaque_senders() { 515 | let person = ("Patrick Walton".to_owned(), 29); 516 | let (sub_tx, sub_rx) = ipc::channel::().unwrap(); 517 | let person_and_sender = (person.clone(), sub_tx.to_opaque()); 518 | let (super_tx, super_rx) = ipc::channel().unwrap(); 519 | super_tx.send(person_and_sender).unwrap(); 520 | let received_person_and_sender = super_rx.recv().unwrap(); 521 | assert_eq!(received_person_and_sender.0, person); 522 | received_person_and_sender 523 | .1 524 | .to::() 525 | .send(person.clone()) 526 | .unwrap(); 527 | let received_person = sub_rx.recv().unwrap(); 528 | assert_eq!(received_person, person); 529 | } 530 | 531 | #[test] 532 | fn try_recv() { 533 | let person = ("Patrick Walton".to_owned(), 29); 534 | let (tx, rx) = ipc::channel().unwrap(); 535 | match rx.try_recv() { 536 | Err(ipc::TryRecvError::Empty) => (), 537 | v => panic!("Expected empty channel err: {:?}", v), 538 | } 539 | tx.send(person.clone()).unwrap(); 540 | let received_person = rx.try_recv().unwrap(); 541 | assert_eq!(person, received_person); 542 | match rx.try_recv() { 543 | Err(ipc::TryRecvError::Empty) => (), 544 | v => panic!("Expected empty channel err: {:?}", v), 545 | } 546 | drop(tx); 547 | match rx.try_recv() { 548 | Err(ipc::TryRecvError::IpcError(ipc::IpcError::Disconnected)) => (), 549 | v => panic!("Expected disconnected err: {:?}", v), 550 | } 551 | } 552 | 553 | #[test] 554 | fn try_recv_timeout() { 555 | let person = ("Jacob Kiesel".to_owned(), 25); 556 | let (tx, rx) = ipc::channel().unwrap(); 557 | let timeout = Duration::from_millis(1000); 558 | let start_recv = Instant::now(); 559 | match rx.try_recv_timeout(timeout) { 560 | Err(ipc::TryRecvError::Empty) => { 561 | assert!(start_recv.elapsed() >= Duration::from_millis(500)) 562 | }, 563 | v => panic!("Expected empty channel err: {:?}", v), 564 | } 565 | tx.send(person.clone()).unwrap(); 566 | let start_recv = Instant::now(); 567 | let received_person = rx.try_recv_timeout(timeout).unwrap(); 568 | assert!(start_recv.elapsed() < timeout); 569 | assert_eq!(person, received_person); 570 | let start_recv = Instant::now(); 571 | match rx.try_recv_timeout(timeout) { 572 | Err(ipc::TryRecvError::Empty) => { 573 | assert!(start_recv.elapsed() >= Duration::from_millis(500)) 574 | }, 575 | v => panic!("Expected empty channel err: {:?}", v), 576 | } 577 | drop(tx); 578 | match rx.try_recv_timeout(timeout) { 579 | Err(ipc::TryRecvError::IpcError(ipc::IpcError::Disconnected)) => (), 580 | v => panic!("Expected disconnected err: {:?}", v), 581 | } 582 | } 583 | 584 | #[test] 585 | fn multiple_paths_to_a_sender() { 586 | let person = ("Patrick Walton".to_owned(), 29); 587 | let (sub_tx, sub_rx) = ipc::channel().unwrap(); 588 | let person_and_sender = Rc::new((person.clone(), sub_tx)); 589 | let send_data = vec![ 590 | person_and_sender.clone(), 591 | person_and_sender.clone(), 592 | person_and_sender.clone(), 593 | ]; 594 | let (super_tx, super_rx) = ipc::channel().unwrap(); 595 | super_tx.send(send_data).unwrap(); 596 | let received_data = super_rx.recv().unwrap(); 597 | assert_eq!(received_data[0].0, person); 598 | assert_eq!(received_data[1].0, person); 599 | assert_eq!(received_data[2].0, person); 600 | received_data[0].1.send(person.clone()).unwrap(); 601 | let received_person = sub_rx.recv().unwrap(); 602 | assert_eq!(received_person, person); 603 | received_data[1].1.send(person.clone()).unwrap(); 604 | let received_person = sub_rx.recv().unwrap(); 605 | assert_eq!(received_person, person); 606 | } 607 | 608 | #[test] 609 | fn bytes() { 610 | // N.B. We're using an odd number of bytes here to expose alignment issues. 611 | let bytes = [1, 2, 3, 4, 5, 6, 7]; 612 | let (tx, rx) = ipc::bytes_channel().unwrap(); 613 | tx.send(&bytes[..]).unwrap(); 614 | let received_bytes = rx.recv().unwrap(); 615 | assert_eq!(&bytes, &received_bytes[..]); 616 | } 617 | 618 | #[test] 619 | fn embedded_bytes_receivers() { 620 | let (sub_tx, sub_rx) = ipc::bytes_channel().unwrap(); 621 | let (super_tx, super_rx) = ipc::channel().unwrap(); 622 | super_tx.send(sub_tx).unwrap(); 623 | let sub_tx = super_rx.recv().unwrap(); 624 | let bytes = [1, 2, 3, 4, 5, 6, 7]; 625 | sub_tx.send(&bytes[..]).unwrap(); 626 | let received_bytes = sub_rx.recv().unwrap(); 627 | assert_eq!(&bytes, &received_bytes[..]); 628 | } 629 | 630 | #[test] 631 | fn test_so_linger() { 632 | let (sender, receiver) = ipc::channel().unwrap(); 633 | sender.send(42).unwrap(); 634 | drop(sender); 635 | let val = match receiver.recv() { 636 | Ok(val) => val, 637 | Err(e) => { 638 | panic!("err: `{:?}`", e); 639 | }, 640 | }; 641 | assert_eq!(val, 42); 642 | } 643 | 644 | #[derive(Clone, Debug, Eq, PartialEq)] 645 | struct HasWeirdSerializer(Option); 646 | 647 | thread_local! { static WEIRD_CHANNEL: RefCell>> = const { RefCell::new(None) } } 648 | 649 | impl Serialize for HasWeirdSerializer { 650 | fn serialize(&self, serializer: S) -> Result 651 | where 652 | S: Serializer, 653 | { 654 | if self.0.is_some() { 655 | WEIRD_CHANNEL.with(|chan| { 656 | chan.borrow() 657 | .as_ref() 658 | .unwrap() 659 | .send(HasWeirdSerializer(None)) 660 | .unwrap(); 661 | }); 662 | } 663 | self.0.serialize(serializer) 664 | } 665 | } 666 | 667 | impl<'de> Deserialize<'de> for HasWeirdSerializer { 668 | fn deserialize(deserializer: D) -> Result 669 | where 670 | D: Deserializer<'de>, 671 | { 672 | Ok(HasWeirdSerializer(Deserialize::deserialize(deserializer)?)) 673 | } 674 | } 675 | 676 | #[test] 677 | fn test_reentrant() { 678 | let null = HasWeirdSerializer(None); 679 | let hello = HasWeirdSerializer(Some(String::from("hello"))); 680 | let (sender, receiver) = ipc::channel().unwrap(); 681 | WEIRD_CHANNEL.with(|chan| { 682 | *chan.borrow_mut() = Some(sender.clone()); 683 | }); 684 | sender.send(hello.clone()).unwrap(); 685 | assert_eq!(null, receiver.recv().unwrap()); 686 | assert_eq!(hello, receiver.recv().unwrap()); 687 | sender.send(null.clone()).unwrap(); 688 | assert_eq!(null, receiver.recv().unwrap()); 689 | } 690 | 691 | #[test] 692 | fn clone_sender_after_receiver_dropped() { 693 | let (tx, rx) = ipc::channel::().unwrap(); 694 | drop(rx); 695 | let _tx2 = tx.clone(); 696 | } 697 | 698 | #[test] 699 | fn transfer_closed_sender() { 700 | let (main_tx, main_rx) = ipc::channel().unwrap(); 701 | let (transfer_tx, _) = ipc::channel::<()>().unwrap(); 702 | assert!(main_tx.send(transfer_tx).is_ok()); 703 | let _transferred_tx = main_rx.recv().unwrap(); 704 | } 705 | 706 | #[cfg(feature = "async")] 707 | #[test] 708 | fn test_receiver_stream() { 709 | use futures_core::task::Context; 710 | use futures_core::task::Poll; 711 | use futures_core::Stream; 712 | use std::pin::Pin; 713 | let (tx, rx) = ipc::channel().unwrap(); 714 | let (waker, count) = futures_test::task::new_count_waker(); 715 | let mut ctx = Context::from_waker(&waker); 716 | let mut stream = rx.to_stream(); 717 | 718 | assert_eq!(count, 0); 719 | match Pin::new(&mut stream).poll_next(&mut ctx) { 720 | Poll::Pending => (), 721 | _ => panic!("Stream shouldn't have data"), 722 | }; 723 | assert_eq!(count, 0); 724 | tx.send(5).unwrap(); 725 | thread::sleep(std::time::Duration::from_millis(1000)); 726 | assert_eq!(count, 1); 727 | match Pin::new(&mut stream).poll_next(&mut ctx) { 728 | Poll::Ready(Some(Ok(5))) => (), 729 | _ => panic!("Stream should have 5"), 730 | }; 731 | } 732 | -------------------------------------------------------------------------------- /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 spawing 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 | --------------------------------------------------------------------------------