https://runebook.dev/ja/docs/rust/reference/inline-assembly :: for reference
cargo build (not using nightly) OK
// main.rs
use std::sync::Arc;
mod stack;
const NUM_LOOP: usize = 1000000; // ループ回数
const NUM_THREADS: usize = 4; // スレッド数
use stack::Stack;
fn main(){
let stack = Arc::new(Stack::<usize>::new());
let mut v = Vec::new();
for i in 0..NUM_THREADS {
let stack0 = stack.clone();
let t = std::thread::spawn(move || {
if i & 1 == 0 {
// 偶数スレッドはpush
for j in 0..NUM_LOOP {
let k = i * NUM_LOOP + j;
stack0.get_mut().push(k);
println!("push: {}", k);
}
println!("finished push: #{}", i);
} else {
// 奇数スレッドはpop
for _ in 0..NUM_LOOP {
loop {
// pop、Noneの場合やり直し
if let Some(k) = stack0.get_mut().pop() {
println!("pop: {}", k);
break;
}
}
}
println!("finished pop: #{}", i);
}
});
v.push(t);
}
for t in v {
t.join().unwrap();
}
assert!(stack.get_mut().pop() == None);
}
// stack.rs :: asm! もはやnightlyでなくなっていた
use std::ptr::null_mut; //most important
use std::arch::asm; //most important
// スタックのノード。リスト構造で管理 <1>
#[repr(C)]
struct Node<T> {
next: *mut Node<T>,
data: T,
}
// スタックの先頭 <2>
#[repr(C)]
pub struct StackHead<T> {
head: *mut Node<T>,
}
impl<T> StackHead<T> {
fn new() -> Self {
StackHead { head: null_mut() }
}
pub fn push(&mut self, v: T) { // <3>
// 追加するノードを作成
let node = Box::new(Node {
next: null_mut(),
data: v,
});
// Box型の値からポインタを取り出す
let ptr = Box::into_raw(node) as *mut u8 as usize;
// ポインタのポインタを取得
// headの格納されているメモリをLL/SC
let head = &mut self.head as *mut *mut Node<T> as *mut u8 as usize;
// LL/SCを用いたpush <4>
unsafe {
asm!("1:
ldxr {next}, [{head}] // next = *head
str {next}, [{ptr}] // *ptr = next
stlxr w10, {ptr}, [{head}] // *head = ptr
// if tmp != 0 then goto 1
cbnz w10, 1b",
next = out(reg) _,
ptr = in(reg) ptr,
head = in(reg) head,
out("w10") _)
};
}
pub fn pop(&mut self) -> Option<T> { // <5>
unsafe {
// ポインタのポインタを取得
// headの格納されているメモリをLL/SC
let head = &mut self.head as *mut *mut Node<T> as *mut u8 as usize;
// popしたノードへのアドレスを格納
let mut result: usize;
// LL/SCを用いたpop <6>
asm!("1:
ldaxr {result}, [{head}] // result = *head
// if result != NULL then goto 2
cbnz {result}, 2f
// if NULL
clrex // clear exclusive
b 3f // goto 3
// if not NULL
2:
ldr {next}, [{result}] // next = *result
stxr w10, {next}, [{head}] // *head = next
// if tmp != 0 then goto 1
cbnz w10, 1b
3:",
next = out(reg) _,
result = out(reg) result,
head = in(reg) head,
out("w10") _);
if result == 0 {
None
} else {
// ポインタをBoxに戻して、中の値をリターン
let ptr = result as *mut u8 as *mut Node<T>;
let head = Box::from_raw(ptr);
Some((*head).data)
}
}
}
}
impl<T> Drop for StackHead<T> {
fn drop(&mut self) {
// データ削除
let mut node = self.head;
while node != null_mut() {
// ポインタをBoxに戻す操作を繰り返す
let n = unsafe { Box::from_raw(node) };
node = n.next;
}
}
}
use std::cell::UnsafeCell;
// StackHeadをUnsafeCellで保持するのみ
pub struct Stack<T> {
data: UnsafeCell<StackHead<T>>,
}
impl<T> Stack<T> {
pub fn new() -> Self {
Stack {
data: UnsafeCell::new(StackHead::new()),
}
}
pub fn get_mut(&self) -> &mut StackHead<T> {
unsafe { &mut *self.data.get() }
}
}
// スレッド間のデータ共有と、チャネルを使った送受信が可能と設定
unsafe impl<T> Sync for Stack<T> {}
unsafe impl<T> Send for Stack<T> {}
0 件のコメント:
コメントを投稿