Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ repository = "https://github.com/Starry-OS/scope-local"

[dependencies]
percpu = "0.2"
spin = { version = "0.9", default-features = false, features = ["lazy"] }
spin = { version = "0.10", default-features = false, features = ["lazy"] }

[dev-dependencies]
ctor = "0.4.2"
ctor = "0.6"
percpu = { version = "0.2", features = ["non-zero-vma"] }
5 changes: 3 additions & 2 deletions percpu.x
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ SECTIONS
. = ALIGN(4K);
_percpu_start = .;
_percpu_end = _percpu_start + SIZEOF(.percpu);
.percpu 0x0 (NOLOAD) : AT(_percpu_start) {
.percpu (NOLOAD) : AT(_percpu_start) {
_percpu_load_start = .;
*(.percpu .percpu.*)
_percpu_load_end = .;
. = _percpu_load_start + ALIGN(64) * CPU_NUM;
_percpu_load_end_aligned = ALIGN(64);
. = _percpu_load_start + (_percpu_load_end_aligned - _percpu_load_start) * CPU_NUM;
}
. = _percpu_end;
}
Expand Down
146 changes: 133 additions & 13 deletions tests/scope_local.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
use std::sync::Arc;
use std::{
panic,
sync::{
Arc,
atomic::{AtomicUsize, Ordering},
},
thread,
};

use ctor::ctor;
use scope_local::{ActiveScope, Scope, scope_local};

#[ctor]
fn init() {
fn init_percpu() {
percpu::init();

unsafe { percpu::write_percpu_reg(percpu::percpu_area_base(0)) };
Expand All @@ -14,18 +21,22 @@ fn init() {
println!("per-CPU area size = {}", percpu::percpu_area_size());
}

scope_local! {
static DATA: usize = 0;
}

#[test]
fn global() {
assert_eq!(*DATA, 0);
fn scope_init() {
scope_local! {
static DATA: usize = 42;
}
assert_eq!(*DATA, 42);
}

#[test]
fn scope() {
scope_local! {
static DATA: usize = 0;
}

let mut scope = Scope::new();
assert_eq!(*DATA, 0);
assert_eq!(*DATA.scope(&scope), 0);

*DATA.scope_mut(&mut scope) = 42;
Expand All @@ -35,14 +46,16 @@ fn scope() {
assert_eq!(*DATA, 42);

ActiveScope::set_global();
}

scope_local! {
static SHARED: Arc<String> = Arc::new("qwq".to_string());
assert_eq!(*DATA, 0);
assert_eq!(*DATA.scope(&scope), 42);
}

#[test]
fn shared() {
fn scope_drop() {
scope_local! {
static SHARED: Arc<()> = Arc::new(());
}

assert_eq!(Arc::strong_count(&SHARED), 1);

{
Expand All @@ -55,3 +68,110 @@ fn shared() {

assert_eq!(Arc::strong_count(&SHARED), 1);
}

#[test]
fn scope_panic_unwind_drop() {
scope_local! {
static SHARED: Arc<()> = Arc::new(());
}

let panic = panic::catch_unwind(|| {
let mut scope = Scope::new();
*SHARED.scope_mut(&mut scope) = SHARED.clone();
assert_eq!(Arc::strong_count(&SHARED), 2);
panic!("panic");
});
assert!(panic.is_err());

assert_eq!(Arc::strong_count(&SHARED), 1);
}

#[test]
fn thread_share_item() {
scope_local! {
static SHARED: Arc<()> = Arc::new(());
}

let handles: Vec<_> = (0..10)
.map(|_| {
thread::spawn(move || {
let global = &*SHARED;

let mut scope = Scope::new();
*SHARED.scope_mut(&mut scope) = global.clone();

unsafe { ActiveScope::set(&scope) };

assert!(Arc::strong_count(&SHARED) >= 2);
assert!(Arc::ptr_eq(&SHARED, global));

ActiveScope::set_global();
})
})
.collect();

for h in handles {
h.join().unwrap();
}

assert_eq!(Arc::strong_count(&SHARED), 1);
}

#[test]
fn thread_share_scope() {
scope_local! {
static SHARED: Arc<()> = Arc::new(());
}

let scope = Arc::new(Scope::new());

let handles: Vec<_> = (0..10)
.map(|_| {
let scope = scope.clone();
thread::spawn(move || {
unsafe { ActiveScope::set(&scope) };
assert_eq!(Arc::strong_count(&SHARED), 1);
assert!(Arc::ptr_eq(&SHARED, &SHARED.scope(&scope)));
ActiveScope::set_global();
})
})
.collect();

for h in handles {
h.join().unwrap();
}

assert_eq!(Arc::strong_count(&SHARED), 1);
assert_eq!(Arc::strong_count(&SHARED.scope(&scope)), 1);
}

#[test]
fn thread_isolation() {
scope_local! {
static DATA: usize = 0;
static DATA2: AtomicUsize = AtomicUsize::new(0);
}

let handles: Vec<_> = (0..10)
.map(|i| {
thread::spawn(move || {
let mut scope = Scope::new();
*DATA.scope_mut(&mut scope) = i;

unsafe { ActiveScope::set(&scope) };
assert_eq!(*DATA, i);

DATA2.store(i, Ordering::Relaxed);

ActiveScope::set_global();
Comment on lines +163 to +166
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this line? I remember percpu delegates to thread local if target_os = "linux"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removing this line will cause a SIGSEGV

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removing this line will cause a SIGSEGV

That's strange, and may indicate a bug in our design.

})
})
.collect();

for h in handles {
h.join().unwrap();
}

assert_eq!(*DATA, 0);
assert_eq!(DATA2.load(Ordering::Relaxed), 0);
}