Skip to content
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

spin-based implementation for no_std #61

Open
matklad opened this issue Oct 8, 2019 · 9 comments
Open

spin-based implementation for no_std #61

matklad opened this issue Oct 8, 2019 · 9 comments

Comments

@matklad
Copy link
Owner

@matklad matklad commented Oct 8, 2019

We currently don't provide sync module in no_std, as it requires OS-level support for blocking. What lazy_static does in this case is that it has a spin feature, which replaces locking with spinning.

I think just silently replacing OS mutexes with busy waiting is the wrong approach, this is an important distinction which should be reflected in the type system.

So, we should instead add an opt-in spin module, which has the same API as sync, but is based on the spin crate. That is, with both std and spin features enabled, the user should be able to use both once_cell::sync and once_cell::spin.

@richardanaya

This comment has been minimized.

Copy link

@richardanaya richardanaya commented Oct 22, 2019

I just ran into needing this tonight and felt bad having to turn to lazy static :( thanks for opening this! once_cell FTW!

DrSensor added a commit to DrSensor/ring that referenced this issue Nov 15, 2019
TODO: make PR after matklad/once_cell#61 resolved
@josephlr

This comment has been minimized.

Copy link

@josephlr josephlr commented Dec 23, 2019

So notes on why we might want this (both in once_cell and eventually merged into libstd/libcore):

  • ring needs a no_std compatible replacement for spin-rs (which is unmaintained), briansmith/ring#921
  • getrandom (a dependency of rand) needs one-time initialization for holding onto file handles, checking CPUID, checking OS support for various functionality, etc... Right now, we implement this on our own but ideally we wouldn't have this custom implementation. As we want to eventually make getrandom part of the libstd (see rust-lang/rust#62079 and rust-lang/rust#62082), it cannot depend on any std features. So using spinlocks is the best bet.
@matklad

This comment has been minimized.

Copy link
Owner Author

@matklad matklad commented Jan 1, 2020

So using spinlocks is the best bet.

I think I disagree with this. Using spin-locks if there's a real operating system around seems bad. If two threads race to run initialization, one thread enters a critical section and is scheduled out of the CPU, the other will be busy waiting for a long time. Moreover, if the first thread is a low-priority one, and the second one has a high priority, we get priority inversion!

Moreover, I don't think std uses blocking at all at the moment when getting random data? I think std only needs randomness for hash maps (is this true?) and there, it uses tls for caching:

https://github.com/rust-lang/rust/blob/e380efa5ecdef714dad72c473fc0933ff4d59283/src/libstd/collections/hash/map.rs#L2459-L2461

Could the getrandom be designed in such a way that it's the client who manages the state?

Something like this

pub struct SysRandom { ... }

impl SysRandom {
    pub fn init() -> SysRandom { ... }
    pub getrandom(&self, dest: &mut [u8]) { ... }
}

The std would then stuff it into a tls, and rand could use a global synchronized OnceCell<SysRandom>.

@matklad

This comment has been minimized.

Copy link
Owner Author

@matklad matklad commented Jan 1, 2020

@josephlr here's a demonstration that, in extremely unfortunate cases, the current spin-lock based implementation in getrandom leads to extremely horrible results: https://github.com/matklad/spin-of-death

@richardanaya

This comment has been minimized.

Copy link

@richardanaya richardanaya commented Jan 1, 2020

@matklad

This comment has been minimized.

Copy link
Owner Author

@matklad matklad commented Jan 1, 2020

@richardanaya I'd love to hear more details about your use-case! I have a theory that one actually never wants a spin lock :)

Am I correct that your use-case is basically "I statically know that there always is exactly one thread, so no synchronization is necessary, and I want to use spin just to work around the annoying compiler errors, although I statically know that we'll never actually spin"?

@matklad

This comment has been minimized.

Copy link
Owner Author

@matklad matklad commented Jan 1, 2020

@richardanaya if my assumption sounds right, could you check if #82 fulfills your use-case?

@mark-i-m

This comment has been minimized.

Copy link

@mark-i-m mark-i-m commented Jan 4, 2020

I just read the blog post. I disagree with the argument about interrupt handlers. Doing something potentially blocking in an interrupt handler is just wrong; interrupt handlers are supposed to be short and do minimal work because they are stealing time from the scheduled task. Personally, I think adding a spin-based no_std feature would be fine, and the implementation can be improved later if needed...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
4 participants
You can’t perform that action at this time.