-
Notifications
You must be signed in to change notification settings - Fork 12.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Port libstd to "no operating system" target #37133
Conversation
Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @brson (or someone else) soon. If any changes to this PR are deemed necessary, please add them as extra commits. This ensures that the reviewer can see what has changed since they last reviewed the code. Due to the way GitHub handles out-of-date commits, this should also make it reasonably obvious what issues have or haven't been addressed. Large or tricky changes may require several passes of review and changes. Please see the contribution instructions for more information. |
Why not remove the modules (or at least everything but the traits), instead of returning errors? And why not add what remains to libcore, or some library that sits between libcore (which is the absolute minimum) and libstd? |
So that existing crates may work with no or minimal modifications. |
I agree that it makes compiling crates for those platforms easier, but making sure they actually work is made harder. You can't rely on the compiler anymore to tell you whether you used something unsupported, you need to find that out by testing, and doing tests is harder than to run the compiler. It makes it also harder for library authors to "port" their libraries to that target. In the ideal case, one could do it like you made your PR: you compile your library to the target and put all features that communicate over the network or similar behind a If your crate is architected properly, the code that uses file I/O outside of the traits should be isolated from the other functionality anyway. Note that I don't suggest that the However, everything that panics or returns an error should be removed. |
doesn't this mean you have a mutex which doesn't deadlock? let x = Mutex::new(5);
let mut y = x.lock();
let mut z = x.lock(); //2 mutable pointers from what i can tell this will panic in debug mode but not in release mode unless i misunderstand |
I suppose I should turn these and others in Mutex/RWLock into actual asserts. Note that panicking in this case is allowed behavior. |
Yeah what we need to do is break I'm currently hoping for a "bottom up approach" which would be something like:
|
Regarding the use of |
Thanks for the PR @jethrogb! I haven't had a chance to read it too much in detail yet, but my initial reaction is along the line of @est31's reaction above. In some sense I'm not sure how this helps "port" existing code to a new platform as it seems like it'd be very difficult to stop calling "always fallible APIs". In some sense this is the purpose of libcore, a library which you can use freely without every worrying about whether it's portable or not. (e.g. it works across all OS environments). Put another way, what does this standard library actually do? If all I/O is stubbed out, it seems like the only thing you really get is libcore plus collections/pointers that abort on OOM, right? We should most certainly have a library for collections/pointers, but I don't necessarily think we'd need a whole new "return an error everywhere" standard library. Similarly the action of porting a library to a "no operating system" target would then entail compiling with Finally, there's been musings of ports like this in the past with the concept of "scenarios" which allow the standard library to be robustly ported various places. That way code is tagged with the "scenario" that it expects to operate within, and you get a compile-time guarantee that you, say, don't use the filesystem and/or network. You could the imagine linker shenanigans to ensure that everything actually compiles in the end. Unfortunately not a lot of movement has been happening on that recently, but it does seem very highly related to this PR itself. |
Two main things:
|
I do agree with @jethrogb that libcore right now is too minimal for projects to really like it, and its not just about collections. I/O traits are really useful. Most rust crates to read/write in some file format are using the Read and Write traits, and it would make things easier to have the traits in webassembly scenarios. Webassembly is completely secluded from the world, afaik it can communicate with the javascript world, and through that you could download files from the net or prompt the user to upload a file and then pass those files on to some crate you use via
A library crate shouldn't create log files without you asking for it. And if it has a separate logging functionality the user has to opt in to, you can surround it with a cfg flag in upstream and it will continue to work. |
A library crate needn't actually create log files for it to have a dependency on File without which it won't compile. |
Yeah these are valid points, the solution is to break up std. |
I can see how that would make more of std available. Could you explain how that helps with using existing crates? |
@jethrogb existing crates downstream of std? Those need to be refactored. I think the pain of refactoring is good here---it makes downstream more conscious of exactly what sort of minimal system they depend on. |
I agree such refactoring is good. I think, though, it is a matter of time-to-market. How many months would it take to get all the necessary changes made? AFAICT it could easily be a year or more. I think it makes sense to have a workaround in place until that happens. |
I agree with @briansmith. In addition, as I was saying before, it's not just a matter of refactoring those crates, but also of convincing those crate's maintainers that they should accept your refactoring. |
I should link #27701; @briansmith and I are veterans of that endless thread. I'd like to think the "scenarios" @alexcrichton mentions are the successor of the "subset std" idea of that thread. When they land, I think they will provide a "lighter" refactor path to the extent they overlap with the crates behind the facade. On the other hand, you all mention time-to-market, but I am in no rush and conversely think its really important we get this right, or at least right before stabilization. Yes it's hard to refactor all those crates but conversely the shape of the no-std ecosystem will be determined by how good a job we do. Post stabilization, any warts or inflexibilities will be impossible to root out (see libc's mess of an interface). |
@jethrogb also I think the lynchpin here for building out the crates behind the facade is jethrogb/rust-core_io#3. (Trace the few issues I've transitively linked to it lately, for example). Getting those associated types is IMO the easiest way to allow for a |
Huh, this seems to always happen with important topics. |
@petrochenkov I don't think @alexcrichton was implying that this PR should be blocked on the "scenarios" idea; I think he was just pointing at something relevant. FWIW, the libs team is taking up working out the "scenarios" design right now -- it's up for discussion today, and notes will be posted to internals. Anyone's welcome to chime in/take leadership on the existing internals thread, of course, or to work in the direction of an RFC. I know that it can be opaque what's being worked on at any given point in time, vs what's up for grabs. We're continually trying to improve our processes (e.g. with rfcbot), and one of the changes we're making starting this cycle is for more "proactive" discussions in the subteam meetings -- as mentioned above, these should result in internals threads which give the broader community a chance to join in as well. We're also talking about doing more to make visible what people are working on. If you have other ideas about improvements, I'd love to hear them. In the meantime, please understand that Rust is a huge project, and it's incredibly difficult to keep it all organized, to stay on top of things in addition to doing productive work. If you feel like something's stalled or being ignored, never hesitate to ping! |
Nominating for libs team discussion on the overall direction/idea. |
cc @rust-lang/libs |
I think this is a worthwhile port. I personally want Rust to be usable in every context, particuraly ones without libc, and I think subsetting the standard library is a reasonable thing to do (the emscripten port could use it). We have quite a bit of refactoring work ahead of us before we can support this type of port though. With this, the recent talk of merging Redox std, and the haiku port, our current platform abstraction in std is at its maintainable limit. I have some ideas for how to get to a better place, where ports like this can be self-contained and not impose huge maintenance burden, and have started at a proof of concept. Unfortunately, as with so many things, the amount of time I can dedicate to it is small. I've discussed my intentions previously in these threads:
I will plan to make a more detailed writeup soon. |
@petrochenkov For me the key is having both scenarios and the facade availible. Basically where possible the facade should be leveraged because its simpler and harder to mess up, but for a few things scenarios are needed. Crucially, I think the facade is more appropriate for exotic platforms, and scenarios more for minute differences between variants of the standard big 3 platforms. So it's great that scenarios is finally happening, but I don't want to block the no-std ecosystem on it. |
We discussed both this PR and the "scenarios" ideas in the libs team meeting today. @alexcrichton will be writing up the scenarios ideas in a new internals thread, which we'll link to here once it's ready. On the whole, the libs team concurs with what many have said on this thread: we think there's a reasonable goal here, but would prefer to fit it into the existing facade story, rather than approach it as a new "workaround" target. More specifically:
@brson is going to write up some more detailed thoughts on how the facade could evolve to handle this and other use-cases, trying to give specific steps that a enterprising contributor could take to make progress. In the meantime, using |
Could you elaborate on what further experimentation you'd like to see here? I've been doing exactly this for the better part of this year and porting anything to work in that environment is extremely painful (e.g. redis-rs/redis-rs@master...jethrogb:core), which is what prompted this PR. |
@jethrogb That's the kind of experimentation we had in mind, yes. The point was that we'd like to head toward facade usage for this, rather than adding a new platform, and the current facade is a starting point that works today. Can you elaborate on what makes it so painful to port? Is it primarily the stuff that's in |
Yes, and there's no prelude, and everything is at a different path (think
And another thing (I feel like I keep repeating myself) is that the current situation is so bad (cfgs everywhere, optional dependencies with weird feature selection) that changes like those for redis-rs above are unlikely to ever be accepted by any sane upstream maintainer. |
The prelude of course can be addressed by having a prelude module that you glob import. The path differences should be addressed by changing the facade crates, just like we did when cleaning up More broadly, something like what you're doing with the |
Hmm, I feel like one the existence of alloc and collections is stable (which I don't see being too far off), having a bunch of facades will be less useful, but this is an unimportant nit. |
@Ericson2314 I'm not assuming that @brson should be writing up his more specific thoughts on this soon. |
I'd like to echo @aturon's sentiment that the mid-facade crates are in no way stable or finalized. I added them when I first made the facade on a whim, and they've basically never changed since then. They could use some serious reorganizing if we plan to seriously use them. Currently, however, they were never seriously intended for the reuse that we're seeing, hence the pains! And also as @aturon mentioned, @brson will comment more here.
This is something I'd like to dig into, but perhaps not on this thread. I think it's bad if we end up in a situation with a jungle of I personally feel that the sentiment that we should shim std stems from the belief that most crates will "just work" if most of std returns and error. I also personally believe that this isn't the case because crates which use std feel the freedom to expand over time. Put another way, if they happen to work with a particular subset today there's no guarantee they will continue to do so as changes are accepted over time. I'd also like to head off concerns about upstreaming this kind of support. If an upstream maintainer does not want to support a platform, then that's a choice that we can't really get around. If, however, the upstream maintainer would like to support a platform, then it's our job to ensure that this support is as ergonomic as possible and easily available. |
Ok, I've typed up my ideas about scenarios. Feedback is certainly welcome! |
Still eagerly waiting for @brson's write-up! In the mean time, reading @HybridEidolon's blog post gave me a half-baked idea. It does however depend on a future version of rust-lang/rfcs#1133 which allows replacing std crates. std should be basically be a facade only. Each of fs, net, stdio, process+env, thread+sync, time should be in its own two crates each. The two crates are "std__" and "std___sys" (e.g. "std_net" contains
An resolved question in this split model is how to deal with |
To be clear, #1133 as written (and my implementation) allows replacing stdlib crates. What the impl doesn't allow is replacing other crates with stdlib crates, but this a temporary restriction until other dep types (i.e. dev-deps, build-deps) are sorted out (not sure it even made it into the RFC).
I'm telling ya, the associated error solves all problems here :) |
Here are my thoughts on the path forward for making std more portable. I suggest reading it all, but the thrust is that platform-specific code should be isolated into platform-specific crates. So in the end, a port like this might be entirely relegated to a |
OMG! Yes yes yes! I've written ralloc, and part of the reason it took so long is because literally everything from the standard library needed complete reimplementation, because I cannot depend on libstd in memory allocators, for obvious reasons. This would reduce the code base by maybe 10%. Not ideal, but certainly a big deal. |
@brson it seems that this PR isn't quite what you had in mind in terms of the grand vision, do you think we should close this and keep iterating or do you feel like this'd be a good intermediate step? (I sort of feel the former, personally) |
Ok, I'm going to close this in favor of the discussion at https://internals.rust-lang.org/t/refactoring-std-for-ultimate-portability/4301 (clearing out the queue) |
This is an attempt to "port" libstd to run in unconventional environments. Unconventional here means: no networking, no filesystem, no environment and in general no system calls. Think OS kernel but different: you do want allocation and collections, you do want floating point numbers, you do want standard Rust abstractions such as
io::Write
, etc. Usingno_std
is really inconvenient in these situations because a lot of std is missing. Examples of such environments are emscripten¹ and Intel SGX.This PR adds a
target_os = "none"
configuration. The approach I've taken is to base everything of off theunix
base, removing the dependency onlibc
, and returning errors from all functions that used libc. I've borrowed some ideas from the Linux and emscripten implementations here and there.In some places the existing unix code needed only minor modifications and
#[cfg()]
additions, in these cases I've modified the files inline. In other cases, it made more sense to reimplement the sys API in a new file. See libstd/sys/none/mod.rs.In general these areas have been modified:
fs
just mostly return io::Error. UNIX-compatible Path/OsString handling is still supportednet
just mostly return io::Error. Addressing structures are still supportedprocess
just mostly return io::Errorenv
just mostly return io::Errorthread
just mostly return io::Error (notestd::thread::spawn
calls unwrap internally...)stdio
use "fake stdio" as is used on Windows when there is no consolesync
replace all primitives with single-threaded primitives without any locking. If you do somehow end up in a multi-threaded environment, this means things are totally and utterly racy and unsafe.time
SystemTime
is like the Linux version and you can do time math, but callingnow
will panic.I'm aware that the current state of the PR is nowhere near merging. I just want to announce my plans and solicit feedback. I already know that the following things need work:
Rather, I'm looking for technical feedback.
You can build this branch like so:
You can then link the
deps
directory to the approriatelib/rustlib
in your rust installation to test building with this target. Here's what you need to supply to make it link:rlibc
or similarextern fn getrandom(buf: *mut u8, len: usize);
I made a sample application that takes this std and adds some Linux system calls around it, it works.
¹: If I understand the state of the emscripten port correctly based on discussions with @brson, if you're trying to use functionality that doesn't exist on the platform you'll just get linker errors. That's of course not very ergonomic.
r? @brson