# Libs-API Meeting 2025-11-18 ###### tags: `Libs Meetings` `Minutes` **Meeting Link**: https://meet.jit.si/rust-libs-meeting-crxoz2at8hiccp7b3ixf89qgxfymlbwr **Attendees**: Amanieu, David, Chris Denton, Tomas, The 8472, GringorenkoPV ## Agenda - Triage - Anything else? ## Triage ### FCPs 1 rust-lang/rfcs T-libs-api FCPs - merge rust.tf/rfc3873 *build\-std: context* - (18 checkboxes left) 14 rust-lang/rust T-libs-api FCPs - merge rust.tf/80437 *Tracking Issue for \`box\_into\_inner\`* - (1 checkboxes left) - merge rust.tf/146792 *Implement \`TryFrom\<char\>\` for \`usize\`.* - (3 checkboxes left) - merge rust.tf/106418 *Implement \`PartialOrd\` and \`Ord\` for \`Discriminant\`* - (2 checkboxes left) - merge rust.tf/146560 *Add the \`cpuid\` target feature* - (5 checkboxes left) - merge rust.tf/116258 *Tracking Issue for explicit\-endian String::from\_utf16* - (1 checkboxes left) - merge rust.tf/139087 *Fallback \`{float}\` to \`f32\` when \`f32: From\<{float}\>\` and add \`impl From\<f16\> for f32\`* - (4 checkboxes left) - close rust.tf/136638 *warn on empty precision* - (3 checkboxes left) - merge rust.tf/98407 *Tracking Issue for \`Exclusive\`* - (1 checkboxes left) - merge rust.tf/145948 *Stabilize 29 RISC\-V target features (\`riscv\_ratified\_v2\`)* - (3 checkboxes left) - merge rust.tf/146660 *Tracking issue for release notes of #146410: Iterator repeat: no infinite loop for \`last\` and \`count\`* - (0 checkboxes left) - merge rust.tf/140808 *Implement Default for &Option* - (2 checkboxes left) - merge rust.tf/141994 *add Iterator::contains* - (2 checkboxes left) - merge rust.tf/135894 *Tracking Issue for \`atomic\_try\_update\`* - (3 checkboxes left) - merge rust.tf/76314 *Tracking Issue for atomic\_from\_mut* - (2 checkboxes left) [estebank (1)](https://rfcbot.rs/fcp/estebank), [SparrowLii (1)](https://rfcbot.rs/fcp/SparrowLii), [mdtro (1)](https://rfcbot.rs/fcp/mdtro), [scottmcm (3)](https://rfcbot.rs/fcp/scottmcm), [Eh2406 (1)](https://rfcbot.rs/fcp/Eh2406), [LawnGnome (1)](https://rfcbot.rs/fcp/LawnGnome), [jackh726 (1)](https://rfcbot.rs/fcp/jackh726), [spastorino (1)](https://rfcbot.rs/fcp/spastorino), [cjgillot (1)](https://rfcbot.rs/fcp/cjgillot), [jtgeibel (1)](https://rfcbot.rs/fcp/jtgeibel), [joshtriplett (4)](https://rfcbot.rs/fcp/joshtriplett), [traviscross (1)](https://rfcbot.rs/fcp/traviscross), [0xPoe (1)](https://rfcbot.rs/fcp/0xPoe), [matthewjasper (1)](https://rfcbot.rs/fcp/matthewjasper), [BurntSushi (9)](https://rfcbot.rs/fcp/BurntSushi), [arlosi (1)](https://rfcbot.rs/fcp/arlosi), [eth3lbert (1)](https://rfcbot.rs/fcp/eth3lbert), [the8472 (9)](https://rfcbot.rs/fcp/the8472), [nikomatsakis (2)](https://rfcbot.rs/fcp/nikomatsakis), [carols10cents (1)](https://rfcbot.rs/fcp/carols10cents), [nagisa (1)](https://rfcbot.rs/fcp/nagisa), [dtolnay (3)](https://rfcbot.rs/fcp/dtolnay), [Amanieu (4)](https://rfcbot.rs/fcp/Amanieu) ### (new change proposal) rust.tf/libs696 *ACP: Add \`Vec::pop\_while\` and \`VecDeque::pop\_{front,back}\_while\`* Amanieu: We have `pop_front_if`. This will repeatedly remove elements while the predicate is true. David: Does the VecDeque case make sense? Amanieu: I don't think it makes sense at all. I don't think it's common enough. You can just do a while loop. David: I agree, none of these are worth adding. Amanieu: I feel we could have these but it's still more explicit to write while loops. The 8472: I don't thin kwe need methods for every control flow. We have actual control flow for this. Amanieu: That said this was submitted 12 minutes ago. David: We should write what the motivating example could be written without it and post that as a comment. Amanieu: The motivating example was shown in the issue. I don't think it saves us much. David: Then we can reject this. ### (new change proposal) rust.tf/libs695 *ACP: Add functions for "compress bits" and "expand bits" to unsigned integers* Amanieu: The name "compress bits" is terrible The 8472: Is this a PDEP thing? Amanieu: Yes, this is PDEP. The motivation is good but the naming is bad. "compress" is too generic. Amanieu: x86 seems to use "parallel_bit_deposit/extract" The 8472: OTher architectures also have have deposit/extract David: I agree with deposit The 8472: First we need to emit the LLVM intrinsic and compile it with a compiler target or with cpu-specific intrinsics. It's a bit like our `mul_add`. We have a mul add but it's not actually faster. Amanieu: I think if we don't have support for that it's just soft floats? Amanieu: I think the argument to wait for the LLVM intninsics is sound. You'd never want to stabilize this without the LLVM intrinsics. David: Would we not want to stabilise it without that? Or add it? Amanieu: We'd not want to stabilise it, but we could add it. David: That's the right order to do this. We can add it and then have a motivation for someone to add it to LLVM. David: For the naming I'm leaning towards the gather/scatter name rather than deposit. Amanieu: SIMD has gather/scatter loads where you pass in a vector of pointers and do a gather load that reads from random addresses into the vector. David: Is that similar enough that we could use those names? Amanieu: Yes and we can also have a doc alias. Amanieu: Ok, I'll block stabilisation on LLVM support. ### (new change proposal) rust.tf/libs693 *Add a method for raw pointers that calls \`ptr::slice\_from\_raw\_parts{\_mut}\`* Amanieu: I saw this on zulip. The 8472: That's what resulted in this ACP. Amanieu: I'm generally in favour. The current naming is "with_len" The 8472: We already have a "with_address" on pointers with provenance. This would be similar. Amanieu: I'd like to see the word "slice" in the name somewhere. The 8472: It wouldn't conflict with the other DSTs, right? Amanieu: This is purely for the purpose of clarification that this is creating a slice. David: In my own code I'm happy with `slice::from_raw_parts` but I understand why people want this. The 8472: A point that often comes up is that working with raw pointers in Rust is tedious. Adding the `slice` to the name would make it more wordy though. But that's mostly a matter of auto completion The 8472: Maybe just `slice_len` as a setter? We already have the `len` method? Amanieu: I was thinking `as_slice` The 8472: `to_slice`? `as_slice` often returns a reference Amanieu: You return a raw pointer. That's kind of a reference. The 8472: `as_slice` already exists for array types. Amanieu: That's going to be a problem. You can't use the name `as_slice`. David: It's unstable so you could rename the existing one to something else. Amanieu: I can see confusion with `as_slice`. Maybe `with_len` would be better. The 8472: `with_len` I'd have concern with future compatibility with other slices. With metadata we have a method that's more general that supports dyn types, slices and tail stuff. David: You'd want a different method name for those. That doesn't replace this. It's fine to have both of those, the slice case and the generic metadata case. The 8472: That one would be `_with_metadata`? David: For that's it's even more clear to me you'd want to use the `from_raw_parts` Amanieu: The problem with `with_metadata` is going to be too generic and you'd have type inference failures. The usize doesn't necessarily mean the output is slice. So I think there's still value in `with_len` even if it's technically identical to `with_metadata`. The 8472: We've made similar additions with methods that only do one thing like casting or returning other properties of the pointer. The 8472: `cast_slice`? Amanieu: It was proposed. The 8472: It would fit into the other methods like `cast_array` Amanieu: Yes, `cast array`'s been proposed. `cast_slice` seems fine considering `cast_array` David: I'm on board with adding `cast_slice` ### (new change proposal) rust.tf/libs692 *ACP: Add \`SystemTime::MIN\` and \`SystemTime::MAX\`* Amanieu: The main concern is that there's no minimum and maximum supported system time but we have methods that imply their existence. I think this is fine to add. The 8472: The problem statement also suggest we should also have saturating versions? Amanieu: Possibly. Amanieu: I think our SystemTime pretty much has fixed format. We're u64 on pretty much all platforms. Chris: Except on Windows. Internally it's kept as Windows time. The 8472: I think in MacOS we used to have something tick-based? Amanieu: It's just Instant. MacOS uses SystemTime. The 8472: Some of them store Durations Amanieu: I'm trying to look for the Windows one Chris: It stores it in nanoseconds. Amanieu: It is in i64 not u64. Chris: yes Amanieu: Is it fine if you give it a day before 1600? Chris: It can The 8472: UEFI does something strange. Chris: The implementation can be decided. Doesn't have to be handled in this meeting. Amanieu: I think this is fine actually The 8472: But we should not specify the the value this has. Chris: Which we already do for Instance. The 8472: We'll want to make sure we do the same here. Amanieu: So, accept? Amanieu: Accept. ### (new change proposal) rust.tf/libs691 *\`std::io::Read\` provides byte\-order reading trait* Amanieu: We discussed this last week. We proposed `read_array` The 8472: The author asked about a generic array later? Amaniou: I think they're talking about generating `T` that's deserialised. Josh: We don't have a method reading T much less `[T; N]`. We don't have any serializing/deserializing method for this. I think maybe they're confused about what we're offering them. I think the answer to their question is that we don't expect this function to support `T`. Josh: If we add something, it'd be more in this line rather than what they're suggesting. Amanieu: I still kind of prefer `read_bytes`. Amanieu: The counterargument was that obviously we're reading bytes. Josh: In the absence of its signature, the name is not evocative of what it does. Amanieu: Yes. Josh: But the signature will make it obvious. The 8472: We generally don't have a good naming for "do this but return an array instead of slice". We went through a bunch of names already. Josh: Fair enough. David: Isthere any connection to draw between the same_transmute traits. There should be a way to spell out with some safe_transmute. It will be bringing that convention. The 8472: That would be more read anything as long as it's initialised with arbitrary bytes. David: I would prefer that over what's described here. Josh: That makes sense but I wouldn't want to block anything on safe_transmute at this point. Amanieu: So what's the consensus. Are we keeping `read_array` or choosing `read_bytes`? Chris: I think all the options are bad. Which is the least bad? The 8472: I think any of the other options are better than bytes. We're also reading bytes from the reader. David: I'm onboard with adding `read_array`. Amanieu: I have a slight preference for `read_bytes` but I'm fine with others. Josh: Slight preference for `read_array`. Amanieu: Okay, it's `read_array`. ### (new change proposal) rust.tf/libs688 *Initial state once cell value* Amanieu: Discussed a few times. I clarified the poison thing. I'm not sure what needs to happen here. Let's skip it for now. ### (new change proposal) rust.tf/libs683 *Expose \`FloatErrorKind\` from private \`core::num::dec2flt\` to public \`core::num::FloatErrorKind\`* The 8472: We asked for which error variants they actually need. Josh: The thing they most strongly want is "I want to parse a floating point and I want to parse something else in the same string". That sounds like it has a stability issue because it would prevent us from making the parser more specific or supporting more things we'de parse as floats. Amanieu: I think relying on partial parsing is extremely brittle and you shouldn't do that. Josh: Especially if you're effectively parsing and retreiving an error and then using the error for something. Amanieu: This no longer makes it a simple enum. The 8472: And if we need to track more information it may also slow parsing down. Amanieu: Yes. Josh: The other usecases (was this empty, it wasn't empty but didn't look like a float) seems reasonably fair. Doing trailing parsing is a very different proposal and brings a fair bit of complexity in. That seems like a domain of a third-party crate. The 8472: And if you do a prefix parsing, I wouldn't even consider that an error. Just consume the tokens and keep parsing. Josh: Someone earlier proposed a `parse_partial` used by FromStr. Amanieu: Proposal: what if we reply we're open to proposing `FloatErrorKind` but we don't want it to carry additional data. Would that still be okay for you? Josh: And in particular, not supporting parse_partial? Amanieu: Yes. Josh: That seems like the right response to do here. The 8472: That returns to the question of why do they need it. Right now the ErrorKind is quite anemic. Amaniou: I think as part of the implementation, it would expand the ErrorKind. Josh: The fact that we have `IntErrorKind` which has multiple cases and is not reasonable seems like a good precedent. ### (new change proposal) rust.tf/libs659 *Generalize ExactSizeIterator to QuantifiedIterator* Amanieu: That's still blocked. ### (new change proposal) rust.tf/libs656 *implement Default for more iterators* Amanieu: Waiting on reply form the author. ### (new change proposal) rust.tf/libs651 *ACP: Add API to write formatted data directly into a \`Vec\<u8\>\`* The 8472: That was the write vec discussion. Amanieu: I need to write a new ACP for the new `write!` macro and the plan to transition to it. ### (stalled change proposal) rust.tf/libs462 *impl fmt::Write for BufWriter* Amanieu: This would be the same thing. ### (stalled change proposal) rust.tf/libs186 *Implementing flatten for \`Option\<&Option\<T\>\>\` and \`Option\<&mut Option\<T\>\>\`* The 8472: Don't we have it already? Yes. Will it conflict? Amanieu: I think it won't conflict. Amanieu: Perhaps we should make the ACP template more explicit full function type signatures of the methods, traits etc. Josh: Agreed. The 8472: When do you need reference of Option? Josh: I've used it some times but mostly I use an option of reference Amanieu: flatten_mut has a different signature. It can't do the const trick. The 8472: I don't know how they're getting the idea Josh: The only rationale, reference to Option is better, but why do you want both? Why don't you just want an option of a reference? Amanieu: Why would you ever need it? The 8472: Generic code. Josh: That's what I've occasionally needed it to. Amanieu: I'm inclined to just have an Option of a reference. Naming? The 8472: just `flatten`? Can we do that? Amanieu: We can do that. It could hurt readability. Josh: I'd have 0 objections to `flatten_ref` or just `flatten` for references. But the one returning a mutable thing should be `flatten_mut`. Amanieu: If we want `flatten_mut` I want `flatten_ref`. Either all three are `flatten` or none is. The 8472: What's the benefit of `flatten_mut`? Josh: Someone made the observation where you might have a mutable case and you want to call `flatten_ref` to intentionally degrade the mutable reference to an immutable reference. That said, I see your point. Josh: I can see the argument of making all three of them `flatten`. Amanieu: I'm happy with flatten_ref and flatten_mut. Josh: Are we comfortable having these? Amanieu: Yes. If people ask specialization I'm fine with it. The 8472: If you have a mutable reference to an `Option` you can `as_ref` it twice so several flattens hanging off a mutable reference might make sense. Amanieu, Josh: didn't follow that, post the playground? https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=4585bc64990458ba6cbe5f0af59969c6 ```rust fn main() { let mut o: Option<u8> = None; let o = &mut o; let a = o.as_ref(); // could be .flatten_ref() let b = o.as_ref(); dbg!(a, b); } ``` Josh: Is the thing you're trying to highlight that just because it's a mutable reference degraded it doesn't have be a unique reference ?? The 8472: Yes. The 8472: So we're going with `flatten_ref` and `flatten_mut`? Josh: Yes, but we also want to say that we want them to consistently return `Option<&Option<T>>` or `Option<&mut Option<T>>` ### (stalled change proposal) rust.tf/libs194 *Fix:Introduce an interface to expose the current \`Command\` captured env var logic* Amanieu: There's a sequencing issue. The inherited environment variables are captured at the ponit where the `Command` is spawned, not when it's built. The 8472: Yes. Amanieu: So we don't know its inherited state yet. Josh: I agree with thatt much. The 8472: One of my replies was to keep the process zombie open and fish the environment variable via procfs. Josh: That would capture the environment variables including any changes made by the child process after it was spawned. The 8472: No, that returns the initial environment when the process was started. `/proc/pid/environ` Amanieu: It's definitely the current environment, not the initial one. Josh: I'm trying to reproduce this right now. Amanieu: `proc/self/environ` is a snapshot of the initial stack setup before the environment variable strings. If you setenv you're not changing the environment variable string, you're changing the pointer that's not pointing to this area. Josh: So in theory you could change the variable that `/proc/pid/environ` but libc doesn't. So you could rewrite the environ bloc and use it to modify `/proc/self/environ` but it's not something an existing program could do or would do. Amanieu: Yes. It's like some programs can change their command name in places like top by modifying `argv[0]` The 8472: The next man page paragraph says that processes can update it. Josh: Which manpage is that? The 8472: proc_pid_environ_5 https://man7.org/linux/man-pages/man5/proc_pid_environ.5.html Josh: Yep, may change the memory location The 8472: The motivating usecase is debugging processes so it may be enough. Josh: It's also not portable. Josh: They're just saying "I can get the environment but I can't get the environment you'd get by calling env clear". We have a way to retrieve some of that information, but not all of the information. getenv will retrieve things that have been clear and return them as none and it will return things that were set. But it won't tell you if you've env cleared it. So yeah, we should be retrieving the env clear flag. That seems fine. Amanieu: Would we want to have a way to say "if I were to execute the command now, which environment would I get?". Josh: Yes, I think we should have "get_env_clear" and also "what the environment would be if I ran the command right now, is this environment". The 8472: That doesn't work due to the pre_spawn hook. Josh: I think we can document that this function returs what the environment would be for the child process and long as it's ?? and the environment variables can't be changed e.g. by pre_exec hook. Josh: If you're executing race conditions between another thread trying to set it. Amanieu: I'm not a fan of "get env clear". If you're using a command as a builder, you should know what to pass. Josh: we had this argument around get_args and get_env etc. The 8472: That's the tracking issue on making the Command ?? Josh: We can solve that when we build a replacement for Command. But right now we have get_arg, get_current_dir, get_program. The only thing you can't get is to get the state of get_env_clear. The 8472: You also can't get `env_removed` Josh: You can. It's in get_envs. Amanieu: Can't we just add "get_resolved_env" Josh: That sounds perfectly reasonable. I wonder if we could use the same iterator VarsOs. Amanieu: Maybe not. I don't think we want to tie it together. For flexibility you want this to be a separate type. Josh: Fair enough. Josh: Proposal: `get_env_clear -> bool` and `get_resolv_env -> Iterator(OsString, OsString)` Amanieu: Yes. Josh: I can write that up. ### (stalled change proposal) rust.tf/libs354 *Add titlecase APIs to \`char\`* Chris: I think we've generally been more against adding more tables. Josh: Would this add a new table or use one we already have? Josh: They say we only provide `is_uppercase` and `is_lowercase`. Josh: I only looked at which characters that are title case. And apparently there's five in all of Unicode. Amanieu: I have 31 entries. https://www.compart.com/en/unicode/category/Lt Josh: Am I missing something? The 8472: The link from Amanieu is a category. Is the one from the ACP also a category? Josh: They're not proposing to add another table. They're proposing to implement this as being title case if it's neither lower case nor upper case. So it may not end up adding another table. Josh: Core already uses a data table for this property. Amanieu: How is the "to title" conversion defined? Josh: This one is defined to specifically looking up the small table. Amanieu: We can do character to title case not string to titlecase (that requires unicode-segmentation) Josh: You're right, it's 31 things. That's going to be a tiny table and we can throw it away when not using it. Amanieu: Look at the very last thing in the solution sketch. It requires a `to_title` conversion but that's not defined here. Josh: They're saying it would require more stable data but because most characters map uppercase to lowercase, they'd only need to store the characters where that's not the case. So only these 31 characters. Amanieu: And the table should be optimised away if not used? Josh: Yes, that's what I said earlier. Amanieu: Ship it. Josh: I'd like to hear from BurntSushing and Manishearth to tell us if this is a really bad idea. I can write this up. ### (stalled change proposal) rust.tf/libs336 *Add \`or\_try\_\*\` variants for HashMap and BTreeMap Entry APIs* Amanieu: If it's already present you get a mutable reference to the value. If not present, it constructs a value and gets a mutable reference to the value. Amanieu: I'd like to make this generic over try. Amanieu: Sure, ship it. Josh: Yes. The 8472: And then also an async version and the async try version? Amanieu+Josh: No. The 8472: Why not the async version? Amanieu: Async makes it difficult to hold onto data. Josh: It will make the early return difficult. The 8472: Yeah. You'd have to be in async context of course. Josh: That too. The 8472: That's a general problem, if you're in an async context and you call closures, you're no longer in async context when you call them. Josh: I can reply. They have a PR but not a tracking issue yet? Amanieu: Yes. Don't forget to make sure they do this generic over try. What are we poiting people to to show how to do it properly? The 8472: try_fold? Amanieu: Yes, `try_fold`. `try_collect` is a more complex one when you need to change the return value. Josh: The new version of `Try` is now on nightly. https://github.com/rust-lang/rust/pull/148725/commits/cc7f844d6d91094f6f43d8acc26ecb473f23a1c7 ### (stalled change proposal) rust.tf/libs483 *\`Box::new\_from\_ref\` for making a \`Box\<T\>\` from a \`&T\` where \`T: CloneToUninit + ?Sized\` (and \`Rc\` and \`Arc\`)* Amanieu: We already have `CloneToUninit`. The intent is being able make a `Box<T>` from a reference that implements it. Josh: That partly seems fine. It should have `clone` in the name if it's cloning. `Box::clone_from_ref`? Amanieu: Yes. Josh: Otherwise, it's completely fine. The 8472: Should we have a trait that says I can be constructed from ?? uninit? Josh: Maybe but now. We want to block it on having inherent traits. Once we have an inherent trait I'd love to have all the `Arc` and `Rc` api done via a trait. Amanieu: This seems to be already stable. Josh: `make_mut` existed before `CloneToUninit` Amanieu: There's an ACP for that. Amanieu: I'm happy to accept this. Josh: Approved, name it `clone_form_ref` and put it under the same feature flag as `CloneToUninit`? Amanieu: Yes. Josh: Sounds good. I can write that up. The 8472: This goes into my "constructors keep multiplying" complain. Josh: Maybe it needs its own feature flag. `CloneToUninit` won't ship until we have specialization. Amanieu: `CloneToUninit` doesn't use specialization. It has a blanket impl for `T: Clone`. Then it implemets a lot of types that don't implement clone. Josh: I see, this is relying more on negative impl. I think you're right that it doesn't need full specialization. It does need negative impls. But I think we're going to stabilise negative impls. Amanieu: But yes, a separate tracking issue. Josh: I will reply (and promptly did so). ### (stalled change proposal) rust.tf/libs133 *Add fmt::Write to io::Write adapter* Josh: Shall we skip this on the basis of the same picture of writing an ACP for? ### (stalled change proposal) rust.tf/libs296 *ACP: Designing an alternative \`FromStr\`* ### (stalled change proposal) rust.tf/libs202 *Support for non\-blocking and best\-effort zero\-copy \`io::copy\`* Josh: They want to use splice, sendfile, copy file range Josh: Splice is terrifying enough that I want people to know they're using it. The 8472: We had this in regular io::copy but nobody ran into issues. But I removed it anyway due to the caveats on sendfile and splice where you must not mutate source data until it's been actually sent. That was difficult to ensure if you don't know how long the data is going to sit in the send buffer. Josh: Having a dedicated new API that explicitly documents the same requiremens that splice does seems like the right answer here. I don't think we should lean on specialization. We should do something that's more specific. The 8472: If we directly go from filedescriptor to file descriptor, we still have the problem that sometimes we need to fall back to userspace copy. Josh: I don't think the API needs to take `BorrowedBuf`. It can just take two FDs and details onhow much you want to move like splice does. If it needs an immediate buffer the same way `io::copy` does. The 8472: Then you can't use it for nonblocking IO. Josh: even if you're doing blocking copy. Reader can succeed, the writer can fail and now you have data. The 8472: IN that case it just throws it away. Josh: You can do the same thing for os::copy. The 8472: If you have save in blocking error you have a non-recoverable error. If you ?? you have a recoverable error but you can only recover if you keep the data. Josh: Makes sense to me to have an os::copy operation that just takes a readable fd, writable fd and size. Copies data from one to the other. And returns an error if either of them fails. And then if we want to provide something for the non-blocking case we could do that separately and it would need design. It's not clear if we want non-blocking or async there. The 8472: Non-blocking is a lower-level primitive. Amanieu: What's the usecase here? Josh: The most common usecase is either pipe to pipe, pipe to file or file to pipe. The 8472: Or file to socket. The 8472: This is frequently used for webservers or anythnig that needs to connect two things to each other, shoveling data. Josh: The pipe case is quite common as well. If you're building a tool that needs to shovel data into pipe asfast as possible (or vice verse) this can be fast because it never goes through userspace. The 8472: Maybe it's complicated enough that it shouldn't be in std. Josh: I'm hoping at some point io_uring has enough zero copy splicing that it would recognise it being able to do an operation with a splice. The 8472: Opinions? Josh: I think we should have a blocking version that's simple and consider th enon-blocking version later. Amanieu: We should not use specialllization for this. And only support FDs. Josh: And we'd have an `AsFd` version for unix and `AsHandle` for windows. The 8472: Then it would be still useful for writing CLI tools but not for webservers. Amanieu: I'd imagine the non-blocking ones would use something like mio. The 8472: You'd use that on the outside. You have a file descriptor ready but you need to send the data through. The 8472: I think if you just want the blocking version, it's not worth it. It's a nice trice and you can optimize some CLI tools but for the broader ecosystem it's not worth it. Josh: I think it's so simple that we can do it. I'm not objecting to the nonblocking issue, just that it needs more design work. For example that a function allocates a buffer but can reuse the buffer for later operations. And that could use io_uring internally. The 8472: I think using io_uring internally would only be useful for the blocking case. For nonblocking you'll want to yield. Josh: You don't really want to recreate the ring, you just want to recreate the specifics of the buffer you're using. The 8472: You could probably return the entire ring with the register buffer. Josh: You could but at that point you're defining an abstraction over io_uring. The 8472: If there's that much exploration left in non-blocking IO I want to retract my own proposal now. Or is anyone eager to have it? Josh: I'd happily use it if it existed. The 8472: What for, Josh? Josh: I have a number of cases that are "I want this data and shove it over a socket". The 8472: And they're just threadpool stuff or something very simple? Josh: One end of the connection is an async. On the other end of the same connection I have something that's just sync. The 8472: That's similar to what I've also built. Josh: Ultimately I'd love for us to have one or more async runtime in the standard library. Josh: By the way io_uring does have splice these days. Amanieu: I've written a coredump handler program that receives a coredump from a pipe/stdin and I need to splice that into a file. It's completely synchronous. Josh: Then you should be able to use the os::copy with what we've sketched out here. The 8472: If we do the synchronous version, do we try to allocate when the source and sink aren't correctly splicable? Josh: I think we should try to use splice and if that doesn't work, use sendfile. There was a discussion in the kernel on making splice work on everything sendfile does. The 8472: That also somewhat limits the availability because you always need an `io::copy` callback. Josh: I don't think we should do the two-step fallback that goes through the pipe. Josh: What gives me hesitation is how do we determine whether to use splice or sendfile. In programs that are directly calling the linux APIs they typically know because they know what FDs they have. The 8472: We have this implement it where we try various approaches and fall back into userspace. Josh: I wonder to what extent we can have a mechanism to determine that we don't repeat the full try out sequence on subsequent connections. The 8472: I proposed the `Copier` struct that could keep state like that. But working with multiple files would make it more complicated. Josh: That sounds like it's more complicated and it shouldn't be in the standard library. The ideal would be to make a single call that makes a single call into the kernel. It takes more design that we're prepared to put in right now. The 8472: I'll close this. Josh: I absolutely do think we should do something in this area. But we're not going to successfully design it in the next 20 minutes. ### (stalled change proposal) rust.tf/libs195 *OS\-level \`thread::Builder\` priority and affinity extensions* Chris: This is still useful to me. For setting thread priority. Josh: Or affinity. Amanieu: But it's so OS-specific Josh: But we can abstract it. The 8472: For the Horizon target (nintendo switch) thread affinity can only be set before the thread is spawned Amanieu: Looking at the PR. Is it just priority and affinity? Josh: Yes. Only priority for Horizon and only affinity for Linux. The 8472: Is the p-thread priority at least somewhat plausible? Josh: There is `pthread_set_sched_prio`. "To the value specified in prio" -- I'm trying to figure out what the The 8472: The BSDs at least also have `get_affinity`. Amanieu: If you look at the pthread_attr man pages. There's set_affinity, get_affinity, etc. Josh: Unfortunately those are non-portable. The 8472: In our code for portable parallelism we're looking at affinity and theres' code for the BSDs, Solaris Amanieu: priority is specified in POSIX Josh: Affinity isn't. And you want non-standard bits if you want it being more precise. Amanieu: there's pthread_addr_set_ched_policy that's posix. Josh: I see. Those seem like enough for what most people want. So there is a way we could do this. Rather than having this be an OS-specific builder extension for threads. It would be a generic extension for threads that takes an opaque type that have portable constructors that are broad hints and non-portable OS-specific ones. Josh: If you want to write affinity, you have to write target-specific code, but all the major targets have it. Amanieu: And ensentially it's just a bitmask of cpus. We can hide that behind a type. Josh: We can have an opaque type for a cpu set. You can specify the CPUs in it and use that to set a portable affinity. Josh: We'd need to have two different interface. (1) portable interface on thread builder that sets priority and affinity. The cpu set doesn't need any nonportable extensions. Thread priority would have portable constructor for simple abstractions and os-specific extensions for "I want to set realtime priority 97". And (2) we'd have a less-portable API to set the priority for a running thread that wouldn't work on Horizon but would work on Linux. The 8472: Sounds reasonable. I'm sceptical of a portable abstraction of setting portable priority. Other people tried this and it looked awkward. We should look at prior art. Josh: I can think of three ways to abstract that. (a) we have a default constructor that sets the default. That's it. (b) we have a very simple constructor of high and low prio that we describe what they done on each os. (c) we provide abstraction that's more granular that e.g. gives you `i32`. And we map that onto os priorities. Josh: I think (c) is a bad idea because people won't want to use that in practice and won't know what to use as the scale. The 8472: Chris, Windows had something else about priorities? If you set a background priority that could change other things to deprioritise? Chris: It could set the disk io, but that can be set separately? I'd be happy for just "high/default/low" as constructors for the crossplatform work. Josh: If I were designing it, I'd give it 5 constructors: default, high, highest, low, lowest. In linux, "low" would be one notch below default, lowest would be the lowes possible and similar for high/highest. The 8472: Would lowest be just 20 or sched_idle? Josh: I had been imagining sched_idle but it's the same argument for realtime but reveres. I think we can stabilise them on different timelines. ?? We could extention types for OSes and then we could have portable abstractions for thread priority much later on. The 8472: I still don't like having the mushy "what does high mean" but the rest sounds good. Josh: That's why I'm saying let's have these and not define the abstractions other than `default`. The 8472: Ok. Josh: For affinity, we'd need to Chris: If you have a lot of processes, your default set is your default portable processes. Josh: Can we have our abstraction define Chris: It should be quite easy to The 8472: What's the limit? Chris+Josh: 64. Josh: Didn't that get fixed in Windows 11? Chris+The8472: Yes. The 8472: If you spawn a thread and set the affinity mask on all 128, what would happen? Josh: On Windows 11 we'd just set it. On 10, the obvious answer would be "do whatever the default is". The 8472: Let's say you set every second bit. Josh: three options (1) error out, that's not supported. (2) just always pick the first one that has it set. (3) pick the one with the most bits set. The 8472: You'd need round robin? Josh: We'd do the right thing on Windows 11, but for a system that's running on Windows 10 *and* has more than 64 CPUs we'd do best effort. Josh: I don't think there's a similar issue on mac and BSD but I don't know for sure. The 8472: That should be part of the implementation. Josh: Sounds like we can build a reasonably abstraction for affinity without OS-specific bits as an API. The 8472: We'd need to count the actually available cores. The user needs to know what affinity to set. Josh: There are two common usecases: 1 CPU and everything. The 8472: For benchmarking etc. putting two threads for hyperthread on the same context Josh: I can write a short response. _Generated by [fully-automatic-rust-libs-team-triage-meeting-agenda-generator](https://github.com/rust-lang/libs-team/tree/main/tools/agenda-generator)_