-
-
Notifications
You must be signed in to change notification settings - Fork 22
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
New zero alloc, no_std compatible implementation #19
Conversation
Okay, I added benchmarks in #20 and the results are significant:
I think having ability to get raw pointer to thread in standard library would help a lot with simple executors like pollster. |
These are some impressive results! I think there's a good case for |
Regarding your question in the description:
No, it's not. And not in a 'maybe, depending on your perspective' way. It is simply UB, but UB that happens to work today with current versions of That said, I don't want to detract from the work you've put into it: I think that's a great case for adding a safe way to do this into |
Oh, totally, I completely agree with this! It was just copied and pasted from the previous PR. Now, I think introduction of
This change brings slight performance regression in
To be fair, I'm not entirely sure why
This has to be manually opted in by the user. They need to pass If By the way, tracking of the rust std changes: So that's about it. What do you think? |
Thanks for the details.
Unfortunately I still wouldn't be happy to include it. UB is pervasive and dangerous. We might as well add a That said, I'm happy for the code to exist in the codebase, provided it's commented out for now, perhaps with a comment explaining the motivation for this. In time, and if |
This implementation adds generic `Parkable` trait that can be implemented on any type. This enables to opt out from the default `Parkable` implementation on `Thread` with `default-features = false`.
Coolio, I've removed |
Thanks! I'll try to find the time to review this over the next few days. |
/// The interface models that of `std::thread`, and many functions, such as | ||
/// [`current`](Parkable::current), [`park`](Parkable::park), and [`unpark`](Parkable::unpark) | ||
/// map to `std::thread` equivalents. | ||
pub trait Parkable: Sized + Clone { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not clear to me that this is an API worth revealing, at least for now. The two implementations it has differ only in whether std
is present, which probably implies that it's better to have an std
feature to selectively enable the two implementations.
impl Wake for Signal { | ||
fn wake(self: Arc<Self>) { | ||
self.notify(); | ||
impl Parkable for () { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would probably use a newtype here like
pub struct Spin;
// - https://github.com/rust-lang/libs-team/issues/200 | ||
// - https://github.com/rust-lang/rust/pull/97524 | ||
#[cfg(feature = "std")] | ||
impl Parkable for Arc<Thread> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Revealing this in a public API seems like a mistake: it's something we'd need to support into the future, even if the associated std feature is merged.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think spinning on futures should be exposed, at least not without ample documentation explaining why you shouldn't do it and an API that makes it non-trivial to instantiate (e.g. Spin::new_consider_something_else()
). Even then, I would still consider removing it entirely.
Spinning on a future is almost never what you want. On actual operating systems it leads to priority inversion issues, and even on embedded systems there is usually a specific instruction or other that lets you spin/block in a less CPU-intensive way.
I've avoiding adding a spinning block_on
to futures-lite
for this reason, and direct people to the spin_on
crate if they really need it. I don't think it's a same pattern to expose.
I'd tend to agree. We also provide a similar warning in the README of That said, I do see utility here. There are several applications in embedded and OS development that effectively just require spinning to wait for some hardware to initialise and an interrupt to be triggered, or the like. In these cases,
This is what |
Hey, sorry for dropping this for too long. After working on a similar system simply using park/unpark, I found out that the system is not robust using
Taken this into account, I'm closing the PR. This also means #8 is broken, and probably only #9 is viable. EDIT: actually, #9 is not viable either, it would need to expose a condvar. |
This is the same as #14, but I originally pushed to
master
on my fork, so I renamed it.My intention is to benchmark this against master, and if results are promising, make a proposal to rust standard library to make the way this PR works safe to use.
PR description copied from #14:
Thanks a lot for this great crate! I wanted to see if one could make
pollster
work inno_std
environments, since for one of my upcoming async libraries having option to run without standard library is key, and I noticed that there already is a PR getting rid of complex synchronization mechanisms like condition variables in #9.The use of thread_locals to avoid locking was really interesting, but that made pollster even harder to support
no_std
, so I took inspiration from that implementation and looked for a way to have the best of all worlds - no dependency onstd
oralloc
.This implementation relies on the fact that the Thread is cloneable and layout-compatible with a pointer (in fact, it's an Arc), thus one may just build a
Waker
out of the thread without unnecessary wrapping behind Arcs.Is this safe? It depends on you, but my view is that it's safe enough, because:
a) Thread relies on having constant memory location and I see no reason why the layout should change anytime soon.
b) Even if the layout changed, it would not lead to unsafety. Instead, the compiler would refuse to compile the crate with
std
feature enabled due to sizes inmem::transmute
mismatching.But I'm not sure what you think, it's a hack, and it would ideally be nice to work towards support for taking a "thread pointer" and construct a thread handle from that in the standard library, but right now there are no such facilities available, thus it is as good as it gets.