Git Product home page Git Product logo

spin-rs's Introduction

spin-rs

Crates.io version docs.rs Build Status

Spin-based synchronization primitives.

This crate provides spin-based versions of the primitives in std::sync. Because synchronization is done through spinning, the primitives are suitable for use in no_std environments.

Before deciding to use spin, we recommend reading this superb blog post by @matklad that discusses the pros and cons of spinlocks. If you have access to std, it's likely that the primitives in std::sync will serve you better except in very specific circumstances.

Features

  • Mutex, RwLock, Once, Lazy and Barrier equivalents
  • Support for no_std environments
  • lock_api compatibility
  • Upgradeable RwLock guards
  • Guards can be sent and shared between threads
  • Guard leaking
  • Ticket locks
  • Different strategies for dealing with contention

Usage

Include the following under the [dependencies] section in your Cargo.toml file.

spin = "x.y"

Example

When calling lock on a Mutex you will get a guard value that provides access to the data. When this guard is dropped, the mutex will become available again.

extern crate spin;
use std::{sync::Arc, thread};

fn main() {
    let counter = Arc::new(spin::Mutex::new(0));

    let thread = thread::spawn({
        let counter = counter.clone();
        move || {
            for _ in 0..100 {
                *counter.lock() += 1;
            }
        }
    });

    for _ in 0..100 {
        *counter.lock() += 1;
    }

    thread.join().unwrap();

    assert_eq!(*counter.lock(), 200);
}

Feature flags

The crate comes with a few feature flags that you may wish to use.

  • mutex enables the Mutex type.

  • spin_mutex enables the SpinMutex type.

  • ticket_mutex enables the TicketMutex type.

  • use_ticket_mutex switches to a ticket lock for the implementation of Mutex. This is recommended only on targets for which ordinary spinning locks perform very badly because it will change the implementation used by other crates that depend on spin.

  • rwlock enables the RwLock type.

  • once enables the Once type.

  • lazy enables the Lazy type.

  • barrier enables the Barrier type.

  • lock_api enables support for lock_api

  • std enables support for thread yielding instead of spinning.

  • portable_atomic enables usage of the portable-atomic crate to support platforms without native atomic operations (Cortex-M0, etc.). The portable_atomic_unsafe_assume_single_core cfg or critical-section feature of portable-atomic crate must also be set by the final binary crate.

    When using the cfg, this can be done by adapting the following snippet to the .cargo/config file:

    [target.<target>]
    rustflags = [ "--cfg", "portable_atomic_unsafe_assume_single_core" ]
    

    Note that this cfg is unsafe by nature, and enabling it for multicore systems is unsound.

    When using the critical-section feature, you need to implement the critical-section implementation that sound for your system by implementing an unsafe trait. See the documentation for the portable-atomic crate for more information.

Remarks

It is often desirable to have a lock shared between threads. Wrapping the lock in an std::sync::Arc is route through which this might be achieved.

Locks provide zero-overhead access to their data when accessed through a mutable reference by using their get_mut methods.

The behaviour of these lock is similar to their namesakes in std::sync. they differ on the following:

  • Locks will not be poisoned in case of failure.
  • Threads will not yield to the OS scheduler when encounter a lock that cannot be accessed. Instead, they will 'spin' in a busy loop until the lock becomes available.

Many of the feature flags listed above are enabled by default. If you're writing a library, we recommend disabling those that you don't use to avoid increasing compilation time for your crate's users. You can do this like so:

[dependencies]
spin = { version = "x.y", default-features = false, features = [...] }

Minimum Safe Rust Version (MSRV)

This crate is guaranteed to compile on a Minimum Safe Rust Version (MSRV) of 1.38.0 and above. This version will not be changed without a minor version bump.

License

spin is distributed under the MIT License, (See LICENSE).

spin-rs's People

Contributors

4ldo2 avatar 64 avatar a1phyr avatar arthurprs avatar bspeice avatar craftspider avatar dingelish avatar diogoteles08 avatar emberian avatar ericson2314 avatar fralalonde avatar joshmcguigan avatar kawadakk avatar messense avatar mvdnes avatar notgull avatar orycterope avatar pedromfedricci avatar phil-opp avatar rafalmiel avatar roblabla avatar ryman avatar strake avatar striezel avatar stupremee avatar taiki-e avatar unknowneclipse avatar usamoi avatar veddan avatar zesterer avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

spin-rs's Issues

Fails with latest rust nighthly.

/home/dpc/.multirust/toolchains/nightly/cargo/registry/src/github.com-0a35038f75765ae4/spin-0.3.1/src/lib.rs:13:1: 13:19 error: an external crate named `core` has already been imported into this module [E0259]
/home/dpc/.multirust/toolchains/nightly/cargo/registry/src/github.com-0a35038f75765ae4/spin-0.3.1/src/lib.rs:13 extern crate core;
                                                                                                                ^~~~~~~~~~~~~~~~~~
/home/dpc/.multirust/toolchains/nightly/cargo/registry/src/github.com-0a35038f75765ae4/spin-0.3.1/src/lib.rs:13:1: 13:19 help: run `rustc --explain E0259` to see a detailed explanation
error: aborting due to previous error

Static initialization

Currently it isn't possible to have a global spin lock because global vars must be statically initialized. Unfortunately the only solution at the moment is to make both fields public so they can be initialized by hand (for this exact reason value is public in anUnsafeCell).

Condition variables

I'd like a wrapper for them, trivial using AtomicBool. Just exposing cpu_relax might be enough for spin-compatible implementations.

Wrong atomic ordering in `Drop` impl for `RwLockWriteGuard` and buggy lock acquisition failure in `RwLock::try_read`

Multiple issues in the RwLock implementation.

The first (and the worst) one is here:

impl<'rwlock, T: ?Sized> Drop for RwLockWriteGuard<'rwlock, T> {
    fn drop(&mut self) {
        self.lock.store(0, Ordering::Relaxed);
    }
}

Use of Ordering::Relaxed is incorrect. The compiler is free to reorder a write behind this, which could lead to two mutable refs being used at the same time (UB). It should be Ordering::Release instead (maybe this was just a typo?). Other parts of this code use the needlessly strict SeqCst ordering, but that's not a soundness issue.

The second that I've found is a bug in the try_read function:

    pub fn try_read(&self) -> Option<RwLockReadGuard<T>>
    {
        let old = (!USIZE_MSB) & self.lock.load(Ordering::Relaxed);
		// readers variable could be incremented here
        let new = old + 1;
        if self.lock.compare_and_swap(old,
                                      new,
                                      Ordering::SeqCst) == old
        {
            Some(RwLockReadGuard {
                lock: &self.lock,
                data: unsafe { & *self.data.get() },
            })
        } else {
            None
        }
    }

The problem is that a different thread could increment the readers count on the indicated line, making the value that the CAS checks for incorrect - it might fail even if there are no writers at all. For example:

    let lock = Arc::new(spin::RwLock::new(0));

    {
        let lock = lock.clone();
        thread::spawn(move || {
            loop {
                lock.try_read().unwrap();
            }
        });
    }

    thread::spawn(move || {
        loop {
            // Increment and decrement readers count in a loop
            lock.read();
        }
    }).join();

This code panics for me, even though it there are no writers so try_read should always succeed.

To be frank, I think it would be better to simply rewrite this whole implementation. The original blog that this was implemented from uses the writers bit to allow for recursive write locking (which this crate doesn't) - I see no reason not to just use some sentinel value like -1 to indicate a writer is holding the lock.

Need a spinlock that never uses std::thread::yield_now

This use case may be a little obscure, so "we don't support that" is a perfectly reasonable response. But, just in case:

On a POSIX system, I have some shared data which I need to access from multiple threads but also from a signal handler. This is generally not possible:

  1. Mutex is based on pthread_mutex, which is not async-signal-safe.
  2. Even if it were, if a thread holding the lock gets a signal delivered, then the handler's attempt to acquire the lock that the thread already holds is forbidden, and will cause deadlocks or panics.

I can work around 2) by simply blocking the signals whenever I hold the lock.

I was hoping to use this crate to work around 1), but the std feature means that I cannot count on this crate's spinlocks not to use std::thread::yield_now, which (oddly) is not async-signal-safe.

Even if I don't enable the std feature myself, crates used by multiple dependents are compiled with the union of the features requested by all those dependents. So if my crate happens to be used with someone else who wants spin with the std feature, then I get libc::sched_yield calls in my signal handlers.

My concerns would be addressed if there were a separate Mutex type that was guaranteed never to use sched_yield or any other non-async-signal-safe system calls, regardless of which features were enabled.

Thanks for your consideration!

Documentation typo for call_once

In the call_once() function documentation, there is a typo at the very end of the 3rd paragraph:

/// When this function returns, it is guaranteed that some initialization
/// has run and completed (it may not be the closure specified). The
/// returned pointer points to the return value of when of those
/// initialization closures.

I think the last sentence should be something like this, but I'm not 100% sure.

The returned pointer points to the value returned by the initialization closure parameter, even on future invocations.

MIRI build in CI is failing

See e. g. https://github.com/mvdnes/spin-rs/actions/runs/3987644011/jobs/6837740845.

 stderr:
error: Undefined Behavior: not granting access to tag <3224> because that would remove [SharedReadOnly for <3170>] which is strongly protected because it is an argument of call 814
   --> /home/runner/work/spin-rs/spin-rs/src/rwlock.rs:536:32
    |
536 |                 data: unsafe { &mut *inner.data.get() },
    |                                ^^^^^^^^^^^^^^^^^^^^^^ not granting access to tag <3224> because that would remove [SharedReadOnly for <3170>] which is strongly protected because it is an argument of call 814
    |
    = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
    = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
help: <3224> was created by a SharedReadWrite retag at offsets [0x8..0xc]
   --> /home/runner/work/spin-rs/spin-rs/src/rwlock.rs:536:38
    |
536 |                 data: unsafe { &mut *inner.data.get() },
    |                                      ^^^^^^^^^^^^^^^^
help: <3170> is this argument
   --> /home/runner/work/spin-rs/spin-rs/src/rwlock.rs:516:29
    |
516 |     fn try_upgrade_internal(self, strong: bool) -> Result<RwLockWriteGuard<'rwlock, T, R>, Self> {
    |                             ^^^^
    = note: BACKTRACE (of the first span):
    = note: inside `spin::rwlock::RwLockUpgradableGuard::<'_, i32>::try_upgrade_internal` at /home/runner/work/spin-rs/spin-rs/src/rwlock.rs:536:32: 536:54
note: inside `spin::rwlock::RwLockUpgradableGuard::<'_, i32>::try_upgrade`
   --> /home/runner/work/spin-rs/spin-rs/src/rwlock.rs:556:9
    |
556 |         self.try_upgrade_internal(true)
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: inside `main::_doctest_main_src_rwlock_rs_545_0`
   --> src/rwlock.rs:549:7
    |
7   | match upgradeable.try_upgrade() {
    |       ^^^^^^^^^^^^^^^^^^^^^^^^^
note: inside `main`
   --> src/rwlock.rs:553:3
    |
11  | } _doctest_main_src_rwlock_rs_545_0() }
    |   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

error: aborting due to previous error

(There are more errors, but that should be enough to get the basic idea.)

The failure seems to be related to stacked borrows, but checking violations of stacked borrows with MIRI is still considered experimental at this moment.

One way to fix this would be to add the -Zmiri-disable-stacked-borrows flag for invokations of MIRI, but according to its documentation that is unsound:

  • -Zmiri-disable-stacked-borrows disables checking the experimental
    [Stacked Borrows] aliasing rules. This can make Miri run faster, but it also
    means no aliasing violations will be detected. Using this flag is unsound
    (but the affected soundness rules are experimental).

Documentation, Runtime comments outdated

This structure behaves a lot like a normal Mutex. There are some differences:

  • It may be used outside the runtime.
    • A normal mutex will fail when used without the runtime, this will just lock
    • When the runtime is present, it will call the deschedule function when appropriate
  • No lock poisoning. When a fail occurs when the lock is held, no guarantees are made

When calling rust functions from bare threads, such as C pthreads, this lock will be very
helpful. In other cases however, you are encouraged to use the locks from the standard
library.

mutex.rs

Most of this is not true anymore. This is referring to the now removed runtime that implemented green-threading for rust. Using std::sync::Mutex in a non-rust thread should be safe. I believe the only difference now a days is the poisoning and that this is no_std.

unwrap function in MutexGuard?

Hello, recently i become writing nostd application but i getting error while trying to unwrap .lock() how it might be fixed?

Why was spin 0.9.6 yanked?

Hi, I've discovered that spin 0.9.6 is yanked, which causes our CI to complain thanks to our use of cargo deny, but I can't find any information about why it was yanked.

There don't seem to be any release notes or even a commit in https://github.com/mvdnes/spin-rs/commits/master that documents the bump to 0.9.7.

Why should it be a priority for me to stop using 0.9.6?

RwLock::try_read is unsound

This safe code creates two references to the save value, one of which is mutable.

let lock = RwLock::new(1);
let read = lock.read();
for _ in 0 .. usize::MAX >> 2 {
  forget(lock.read());
}
let mut write = lock.write();

(&*read, &mut *write)

It will succeed, due to overflow in internal atomic value.

There must be protection against overflows in locking functions.
For reference, see https://docs.rs/atomicell/0.1.2/src/atomicell/borrow.rs.html#95

Inconsistent feature name

Hi, very good crate, it as undergone a strong improvement from the version 0.5 that is use.

As i said in the title i noticed that the lock_api feature is named lock_api1 into the code, making impossible for the users to enable that feature.

Keep it up!

Use PAUSE instruction?

In obtain_lock, would it make sense (on x86) to use the PAUSE instruction inside the loop? Looking at the Intel manuals, it seems to be designed for this kind of situation.

Once is not panic-robust

Right now if the builder panics, all concurrent and future Once::call_once calls will be stuck forever.

Request: Add support for atomic-polyfill

The Game Boy Advance is an ARMv4T platform with tier 3 support via the target triple thumbv4t-none-eabi. It's a single-threaded processor with no atomic instructions, so you have to use the atomic-polyfill crate and provide a custom implementation of a critical section which disables interrupts for the duration of an atomic operation.

This crate assumes it can find atomic types in the core library and therefore fails on this platform. Would you be willing to add a feature flag to support atomic-polyfill?

Interest in crate maintenance.

Hello @mvdnes I'm sorry to hear that you've lost interest in maintaining this crate ๐Ÿ˜ž

I've found this crate useful and took an interest in it when I used it for a toy kernel project of mine so I'd like to offer taking up responsibility of maintaining this crate so feel free to send me a collaborator invitation or a repository transfer request ๐Ÿ˜„

Greedy read lock for RwLock

I would like to be able to do a greedy readlock on the RwLock.

What I mean by a "greedy" readlock, is that when I call "read" on the RwLock, it increments the reader count (locking out future writers), but the read guard it returns will spin in the deref so that the data access is still safe.

I don't know if this would be of interest to anyone else, but I have hacked away at the RwLock implementation to fit my needs: https://gist.github.com/Visic/5c672c750e1a6e16590d9cd7dd11d1c8

My specific use case is that I need to be able to lock in the current state when some event happens (without blocking that thread), and then leaving it up to the future consumers of the lock to deal with the potential waiting. This also has an added benefit that waiting at that point is less likely, since if the write lock had been taken before, the writer might have finished before you ever actually access the data.

A further improvement could be to implement some sort of "try_deref" on the readlock, so that the readers aren't Forced to wait if they want to poll for the final result.

If this isn't of interest for this crate, please feel free to close the issue.

Cannot borrow `lazy::Lazy<T>` mutably?

I have the following type:

type IrqList = FnvIndexMap<u8, MiniVec<InterruptHandler>, 256>;
pub type InterruptHandler = Box<dyn Fn(InterruptStackFrameValue) + Send + Sync>;

However, doing this:

static IRQ_FUNCS: Lazy<IrqList> = Lazy::new(|| {
let mut table = IrqList::new();
(0 .. u8::MAX).for_each(|i| {
let v = MiniVec::<InterruptHandler>::new();
if table.insert(i, v).is_err() {
panic!("Cannot add ISR function table for interrupt {}!", i);
}
});
table
});

And then trying to modify that mutably gives me the error:

error[E0596]: cannot borrow data in a dereference of `spin::lazy::Lazy<IndexMap<u8, MiniVec<alloc::boxed::Box<dyn Fn(InterruptStackFrameValue) + Send + Sync>>, hash32::BuildHasherDefault<hash32::fnv::Hasher>, 256_usize>, fn() -> IndexMap<u8, MiniVec<alloc::boxed::Box<dyn Fn(InterruptStackFrameValue) + Send + Sync>>, hash32::BuildHasherDefault<hash32::fnv::Hasher>, 256_usize>>` as mutable
   --> src/interrupts.rs:796:26
    |
796 |     if let Some(funcs) = IRQ_FUNCS.get_mut(&interrupt) {
    |                          ^^^^^^^^^ cannot borrow as mutable
    |
    = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `spin::lazy::Lazy<IndexMap<u8, MiniVec<alloc::boxed::Box<dyn Fn(InterruptStackFrameValue) + Send + Sync>>, hash32::BuildHasherDefault<hash32::fnv::Hasher>, 256_usize>, fn() -> IndexMap<u8, MiniVec<alloc::boxed::Box<dyn Fn(InterruptStackFrameValue) + Send + Sync>>, hash32::BuildHasherDefault<hash32::fnv::Hasher>, 256_usize>>`

Is this a problem with spin::lazy::Lazy itself? Or is this a problem with whatever is within it? This used to work with lazy_static but doesn't work like this for some reason. And DerefMut is not implemented for spin::lazy::Lazy. Is a lock more appropriate here?

Provide alternatives for `std::lazy::SyncLazy` type?

RFC rust-lang/rfcs#2788 introduced a (still unstable) std::lazy module. While the Lazy and OnceCell types of this module are also available in core, their Sync counterparts are not because of their Mutex dependency.

The spin crate already provides a spin::Once as an alternative to the std::lazy::SyncOnceCell type (albeit with a different API). It would be useful if it would also provide an alternative to the std::lazy::SyncLazy type. This way, we could avoid depending on the lazy_static crate.

Does Once require the inner type to be Sync for Send?

I'm trying to make a type that can initialize once, but get modified later. Since Once doesn't have any methods to get a mutable reference to the inner value, I had to wrap it in a RefCell. Since I'm handling the syncronisation elsewhere, I only need the type to implement Send. I can understand needing Send and Sync to implement Sync, but does the inner type really need Sync in order to implement Send? Alternatively, since I have a mutable reference to the Once value, it would be nice to have a get_mut like method that requires a mutable/unique reference.

Minor version release (0.5.1)?

Wonder if 0.5.1 could be released so as to get rid of build warnings (re: atomic bool init that was recently fixedin the master)?

Remove `panicked` field from Once's Finish guard

The question is (albeit probably very unlikely to make a difference), do we even need to perform that store? Because if panic = 'unwind' then IIRC panics will only execute destructors and not code, so shouldn't we be able to simply do core::mem::forget(finish)?

Originally posted by @4lDO2 in #109 (comment)

Compilation failure on nightly

As of rustc 1.22.0-nightly (cfcac3720 2017-09-17), spin fails to build on nightly:

error: `<core::cell::UnsafeCell<T>>::new` is not yet stable as a const fn
   --> ~/.cargo/registry/src/github.com-1ecc6299db9ec823/spin-0.4.5/src/mutex.rs:123:19
    |
123 |             data: UnsafeCell::new(user_data),
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: in Nightly builds, add `#![feature(const_unsafe_cell_new)]` to the crate attributes to enable

error: `<core::cell::UnsafeCell<T>>::new` is not yet stable as a const fn
   --> ~/.cargo/registry/src/github.com-1ecc6299db9ec823/spin-0.4.5/src/rw_lock.rs:103:19
    |
103 |             data: UnsafeCell::new(user_data),
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: in Nightly builds, add `#![feature(const_unsafe_cell_new)]` to the crate attributes to enable

error: `core::sync::atomic::AtomicUsize::new` is not yet stable as a const fn
  --> ~/.cargo/registry/src/github.com-1ecc6299db9ec823/spin-0.4.5/src/once.rs:56:20
   |
56 |             state: AtomicUsize::new(INCOMPLETE),
   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = help: in Nightly builds, add `#![feature(const_atomic_usize_new)]` to the crate attributes to enable

error: `<core::cell::UnsafeCell<T>>::new` is not yet stable as a const fn
  --> ~/.cargo/registry/src/github.com-1ecc6299db9ec823/spin-0.4.5/src/once.rs:57:19
   |
57 |             data: UnsafeCell::new(None),
   |                   ^^^^^^^^^^^^^^^^^^^^^
   |
   = help: in Nightly builds, add `#![feature(const_unsafe_cell_new)]` to the crate attributes to enable

Releasing a new version

Several PRs were merged recently that add a significant number of features to the crate. A new version for crates.io would be welcome.

CI: Set minimal permissions on GitHub Workflow

Hello!

I'd like to suggest the definition of minimal permissions on your workflow, as it would harden your security agains supply-chain attacks. I see that you have only one workflow, the rust.yml, but it does not specify the permissions for its jobs, letting their privileges determined by GitHub's defaults. By defining minimal permissions you would be secured against erroneous or malicious actions from external jobs you call from your workflow. It's specially important for the case they get compromised, for example.

Setting minimum permissions for workflows is recommended by GitHub itself and also by other security tools, such as Scorecards and StepSecurity.

Let me know what you think about this. I'd be happy to raise a PR with the changes if you agree.

Context

I'm Diogo and I work on Google's Open Source Security Team(GOSST) in cooperation with the Open Source Security Foundation (OpenSSF). My core job is to suggest and implement security changes on widely used open source projects ๐Ÿ˜Š

Adopt and expose exponential backoff spinning

@Amanieu's parking_lot crate has a SpinWait module that implements exponential backoff. This would be a useful addition to the current implementation, and could also be exposed on its own. The latter might be useful when implementing other synchronization primitives that only need the spinning, and not the locking. There's been some discussion on this on the parking_lot issue tracker.

The biggest hurdle to adoption is that SpinWait currently yields after spinning for a while, which wouldn't work with no_std. As discussed on the parking_lot issue, this could be put behind a feature flag, or maybe even removed altogether and left to the users of the module.

Rename repo?

Github has decent redirect support, so it would be probably safe to rename the repo to just spin.

Relicense under dual MIT/Apache-2.0

Hello. I'd like to use this crate in Robigalia.
However, for that,

TL;DR the Rust ecosystem is largely Apache-2.0. Being available under that
license is good for interoperation. The MIT license as an add-on can be nice
for GPLv2 projects to use your code.

Why?

The MIT license requires reproducing countless copies of the same copyright
header with different names in the copyright field, for every MIT library in
use. The Apache license does not have this drawback. However, this is not the
primary motivation for me creating these issues. The Apache license also has
protections from patent trolls and an explicit contribution licensing clause.
However, the Apache license is incompatible with GPLv2. This is why Rust is
dual-licensed as MIT/Apache (the "primary" license being Apache, MIT only for
GPLv2 compat), and doing so would be wise for this project. This also makes
this crate suitable for inclusion and unrestricted sharing in the Rust
standard distribution and other projects using dual MIT/Apache, such as my
personal ulterior motive, the Robigalia project.

Some ask, "Does this really apply to binary redistributions? Does MIT really
require reproducing the whole thing?" I'm not a lawyer, and I can't give legal
advice, but some Google Android apps include open source attributions using
this interpretation. Others also agree with
it
.

But, again, the copyright notice redistribution is not the primary motivation
for the dual-licensing. Stronger protections to licensees and better
interoperation with the wider Rust ecosystem is.

How?

To do this, get explicit approval from each contributor of copyrightable
work (as not all contributions qualify for copyright, due to not being a
"creative work", e.g. a typo fix. The advice the FSF uses is "less than
15 lines") and then add the following to your README:

## License

Licensed under either of

 * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
 * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)

at your option.

### Contribution

Unless you explicitly state otherwise, any contribution intentionally
submitted for inclusion in the work by you, as defined in the Apache-2.0
license, shall be dual licensed as above, without any additional terms
or conditions.

and in your license headers, if you have them, use the following boilerplate
(based on that used in Rust):

// Copyright 2016 spin-rs Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

It's commonly asked whether license headers are required. I'm not comfortable
making an official recommendation either way, but the Apache license
recommends it in their appendix on how to use the license.

Be sure to add the relevant LICENSE-{MIT,APACHE} files. You can copy these
from the Rust repo for a plain-text
version.

And don't forget to update the license metadata in your Cargo.toml to:

license = "MIT OR Apache-2.0"

I'll be going through projects which agree to be relicensed and have approval
by the necessary contributors and doing this changes, so feel free to leave
the heavy lifting to me!

Contributor checkoff

To agree to relicensing, comment with:

I license past and future contributions under the dual MIT/Apache-2.0 license, allowing licensees to chose either at their option.

Or, if you're a contributor, you can check the box in this repo next to your
name. My scripts will pick this exact phrase up and check your checkbox, but
I'll come through and manually review this issue later as well.

portable_atomic feature does not compile

error[E0599]: no method named `compare_exchange_weak` found for struct `AtomicBool` in the current scope
   --> /home/asaveau/.cargo/registry/src/index.crates.io-6f17d22bba15001f/spin-0.9.5/src/mutex/spin.rs:179:14
    |
179 |             .compare_exchange_weak(false, true, Ordering::Acquire, Ordering::Relaxed)
    |              ^^^^^^^^^^^^^^^^^^^^^ method not found in `AtomicBool`

error[E0599]: no method named `compare_exchange` found for struct `AtomicBool` in the current scope
   --> /home/asaveau/.cargo/registry/src/index.crates.io-6f17d22bba15001f/spin-0.9.5/src/mutex/spin.rs:239:14
    |
239 |             .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
    |              ^^^^^^^^^^^^^^^^ method not found in `AtomicBool`

For more information about this error, try `rustc --explain E0599`.
error: could not compile `spin` due to 2 previous errors

On riscv32i-unknown-none-elf

Changing Ownership

Given that there are several feature requests open in the issues and that this project lies at the heart of many Rust projects, would you be open to transferring ownership of the repository & crate to someone else?

`Once::call_once` doesn't guarantee that the given closure is called only if this is the first time `call_once` has been called.

This test fails with a certain probability.

// insert this in once.rs:684
    #[test]
    fn extra() {
        use std::sync::Arc;
        use std::sync::atomic::AtomicUsize;
        use std::time::Duration;
        let share = Arc::new(AtomicUsize::new(0));
        let once = Arc::new(Once::<_, Spin>::new());
        let mut hs = Vec::new();
        for _ in 0..10 {
            let h = thread::spawn({
                let share = share.clone();
                let once = once.clone();
                move || {
                    thread::sleep(Duration::from_millis(10));
                    once.call_once(|| {
                        share.fetch_add(1, Ordering::SeqCst);
                    });
                }
            });
            hs.push(h);
        }
        for h in hs {
            let _ = h.join();
        }
        assert_eq!(1, share.load(Ordering::SeqCst));
    }

Unsoundness in `Once::try_call_once()`

UB when try_call_once returns an error while multiple threads are attempting to access the Once. On debug builds this panics, but in release this falls back to unreachable() which is UB.

It looks like the cause is that the function expects that the state can only go from running->[complete, panicked] and doesn't take into account that it can go back to incomplete if the once function returns an error. The solution here would be retrying rather than hitting the unreachable.

Minimal Reproduce:

use std::{thread, time::Duration};

use spin::Once;

fn main() {
    let once = Once::new();

    thread::scope(|s| {
        s.spawn(|| {
            _ = once.try_call_once(|| {
                thread::sleep(Duration::from_secs(1));
                Err(())
            });
        });
        s.spawn(|| {
            thread::sleep(Duration::from_millis(10));
            once.call_once(|| {});
        });
    });
}

Offer more efficient `cpu_relax` on certain platforms

For example, on Linux, we could use the SCHED_YIELD system call (code 24) that would yield to the OS scheduler instead of using pause for all x86 implementations. This also has the benefit of supporting more platforms efficiently.

Add some good examples

It would be nice to have some good example usages for each of the primitives in this crate.

If you, dear reader, happen to be interested in writing one then we'll be very happy to accept proposals!

Unsafe impls

While working on Once, I noticed that our unsafe Send and Sync impls are different from std---I am not sure why or whether its sound. [I ended up going with the RwLock ones for Once, since both give access to a &T to potentially multiple threads.]

Debug trait

I understand it's a no_std crate, but adding a Debug trait would be trivial and it'd greatly expand it's use case in non no_std projects.

Could not find `hint` in `core` , what 's happening?

Compiling spin v0.4.10
error[E0432]: unresolved import core::hint
--> C:\Users\xiaolong.cargo\registry\src\mirrors.ustc.edu.cn-61ef6e0cd06fb9b8\spin-0.4.10\src\once.rs:48:11
|
48 | use core::hint::unreachable_unchecked as unreachable;
| ^^^^ Could not find hint in core

error: aborting due to previous error

If you want more information on this error, try using "rustc --explain E0432"
error: Could not compile spin.

To learn more, run the command again with --verbose.

Switching to GitHub actions

@mvdnes I'm switching the repository CI over to GitHub actions. This will make CI automatically integrated with PRs and forks. I'm also switching the documentation in Cargo.toml over to the default docs.rs domain to avoid those using stable releases being redirected to unstable docs. I will, however, keep the documentation badge that redirects to your own URL to allow nightly docs to work fine.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.