Skip to content

Commit 308b887

Browse files
wan9chiGPT-5.6
andcommitted
fix(fspy): use memfd for Linux shared memory
Co-authored-by: GPT-5.6 <gpt-5.6@openai.com>
1 parent aaaf01b commit 308b887

11 files changed

Lines changed: 728 additions & 86 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
- **Fixed** Windows automatic input tracking now records executable image reads when process creation performs the lookup inside `NtCreateUserProcess` ([#518](https://github.com/voidzero-dev/vite-task/pull/518)).
44
- **Fixed** Failures while waiting for a started task process to exit no longer incorrectly say the process failed to spawn ([#515](https://github.com/voidzero-dev/vite-task/pull/515)).
55
- **Fixed** Missing env vars requested through `@voidzero-dev/vite-task-client` now return `undefined` instead of `null`, preserving Vite production `NODE_ENV` semantics when builds run through `vp run` ([#508](https://github.com/voidzero-dev/vite-task/pull/508)).
6+
- **Fixed** Linux file-access tracking no longer consumes the `/dev/shm` mount used by containers and Kubernetes runners ([#353](https://github.com/voidzero-dev/vite-task/issues/353)).
67
- **Fixed** Windows builds no longer hang on CI when a `node_modules/.bin` `.cmd` shim is routed through PowerShell: the npm/pnpm/yarn `.ps1` wrappers read stdin and block forever on a non-TTY pipe, so the PowerShell rewrite is now skipped when stdin is not an interactive terminal, falling back to the `.cmd` (which never reads stdin) ([#491](https://github.com/voidzero-dev/vite-task/pull/491)).
78
- **Added** First-party support for caching `vite build` with zero cache config, giving Vite projects correct cache hits out of the box ([vitejs/vite#22453](https://github.com/vitejs/vite/pull/22453)).
89
- **Added** Support for specifying tasks from dependency packages in `dependsOn`, such as `dependsOn: [{ "task": "build", "from": "dependencies" }]` ([#479](https://github.com/voidzero-dev/vite-task/pull/479)).

Cargo.lock

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ ref-cast = "1.0.24"
115115
regex = "1.11.3"
116116
rusqlite = "0.39.0"
117117
rustc-hash = "2.1.1"
118+
rustix = "1.1"
118119
# SeccompAction::UserNotif (SECCOMP_RET_USER_NOTIF) was added after the latest published release (v0.5.0)
119120
seccompiler = { git = "https://github.com/rust-vmm/seccompiler", rev = "08587106340b8e3cb361c7561411510039436857" }
120121
serde = "1.0.219"

crates/fspy_shared/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ ctor = { workspace = true }
2828
rustc-hash = { workspace = true }
2929
subprocess_test = { workspace = true }
3030

31+
[target.'cfg(target_os = "linux")'.dev-dependencies]
32+
tokio = { workspace = true, features = ["net", "rt-multi-thread", "time"] }
33+
3134
[lints]
3235
workspace = true
3336

crates/fspy_shared/src/ipc/channel/mod.rs

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -139,9 +139,12 @@ impl Deref for Sender {
139139
}
140140
}
141141

142-
#[expect(
143-
clippy::non_send_fields_in_send_ty,
144-
reason = "`Sender` holds a shared file lock that ensures there's no reader, so `shm` can be safely written to"
142+
#[cfg_attr(
143+
not(target_os = "linux"),
144+
expect(
145+
clippy::non_send_fields_in_send_ty,
146+
reason = "`Sender` holds a shared file lock that ensures there's no reader, so `shm` can be safely written to"
147+
)
145148
)]
146149
/// SAFETY: `Sender` holds a shared file lock that ensures there's no reader, so `shm` can be safely written to.
147150
unsafe impl Send for Sender {}
@@ -157,9 +160,12 @@ pub struct Receiver {
157160
shm: Shm,
158161
}
159162

160-
#[expect(
161-
clippy::non_send_fields_in_send_ty,
162-
reason = "Receiver doesn't read or write `shm`. It only pass it to `ReceiverLockGuard` under the lock"
163+
#[cfg_attr(
164+
not(target_os = "linux"),
165+
expect(
166+
clippy::non_send_fields_in_send_ty,
167+
reason = "Receiver doesn't read or write `shm`. It only passes it to `ReceiverLockGuard` under the lock"
168+
)
163169
)]
164170
/// SAFETY: `Receiver` doesn't read or write `shm`. It only passes it to `ReceiverLockGuard` under the lock.
165171
unsafe impl Send for Receiver {}
@@ -229,6 +235,8 @@ mod tests {
229235
#[test]
230236
fn smoke() {
231237
let created = channel(100).unwrap();
238+
#[cfg(target_os = "linux")]
239+
let (broker_runtime, broker_handle) = start_test_broker(created.broker);
232240
let conf = created.conf;
233241
let receiver = created.receiver;
234242
let cmd = command_for_fn!(conf, |conf: ChannelConf| {
@@ -239,6 +247,9 @@ mod tests {
239247
});
240248
assert!(std::process::Command::from(cmd).status().unwrap().success());
241249

250+
#[cfg(target_os = "linux")]
251+
broker_runtime.block_on(broker_handle.stop()).unwrap();
252+
242253
let lock = receiver.lock().unwrap();
243254
let mut frames = lock.iter_frames();
244255

@@ -281,6 +292,8 @@ mod tests {
281292
#[test]
282293
fn concurrent_senders() {
283294
let created = channel(8192).unwrap();
295+
#[cfg(target_os = "linux")]
296+
let (broker_runtime, broker_handle) = start_test_broker(created.broker);
284297
let conf = created.conf;
285298
let receiver = created.receiver;
286299
for i in 0u16..200 {
@@ -300,6 +313,8 @@ mod tests {
300313
B(&output.stderr)
301314
);
302315
}
316+
#[cfg(target_os = "linux")]
317+
broker_runtime.block_on(broker_handle.stop()).unwrap();
303318
let lock = receiver.lock().unwrap();
304319
let mut received_values: Vec<u16> = lock
305320
.iter_frames()
@@ -308,4 +323,17 @@ mod tests {
308323
received_values.sort_unstable();
309324
assert_eq!(received_values, (0u16..200).collect::<Vec<u16>>());
310325
}
326+
327+
#[cfg(target_os = "linux")]
328+
fn start_test_broker(
329+
broker: ShmBroker,
330+
) -> (tokio::runtime::Runtime, fspy_shm::ShmBrokerHandle) {
331+
let runtime =
332+
tokio::runtime::Builder::new_multi_thread().enable_io().enable_time().build().unwrap();
333+
let handle = {
334+
let _guard = runtime.enter();
335+
broker.start()
336+
};
337+
(runtime, handle)
338+
}
311339
}

crates/fspy_shared/src/ipc/channel/shm_io.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -674,7 +674,13 @@ mod tests {
674674

675675
let created = fspy_shm::create(SHM_SIZE).unwrap();
676676
#[cfg(target_os = "linux")]
677-
let broker_handle = created.broker.start();
677+
let broker_runtime =
678+
tokio::runtime::Builder::new_multi_thread().enable_io().enable_time().build().unwrap();
679+
#[cfg(target_os = "linux")]
680+
let broker_handle = {
681+
let _guard = broker_runtime.enter();
682+
created.broker.start()
683+
};
678684
let shm = created.shm;
679685
let shm_name = shm.id().to_owned();
680686

@@ -704,7 +710,7 @@ mod tests {
704710
}
705711

706712
#[cfg(target_os = "linux")]
707-
drop(broker_handle);
713+
broker_runtime.block_on(broker_handle.stop()).unwrap();
708714

709715
// SAFETY: All child processes have exited (waited above), so no concurrent writers exist.
710716
// The shared memory is valid and fully written.

crates/fspy_shm/Cargo.toml

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,24 @@ license.workspace = true
77
publish = false
88
rust-version.workspace = true
99

10-
[dependencies]
10+
[target.'cfg(not(target_os = "linux"))'.dependencies]
1111
shared_memory = { workspace = true, features = ["logging"] }
1212

13+
[target.'cfg(target_os = "linux")'.dependencies]
14+
base64 = { workspace = true }
15+
memmap2 = { workspace = true }
16+
rustix = { workspace = true, features = ["fs", "net"] }
17+
tempfile = { workspace = true }
18+
tokio = { workspace = true, features = ["macros", "net", "rt", "sync", "time"] }
19+
uuid = { workspace = true, features = ["v4"] }
20+
1321
[dev-dependencies]
1422
ctor = { workspace = true }
1523
subprocess_test = { workspace = true }
1624

25+
[target.'cfg(target_os = "linux")'.dev-dependencies]
26+
tokio = { workspace = true, features = ["macros", "rt-multi-thread"] }
27+
1728
[lints]
1829
workspace = true
1930

0 commit comments

Comments
 (0)