Падение программы с (exit code: 0xc0000005, STATUS_ACCESS_VIOLATION)
Это код.
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use std::alloc::{alloc, dealloc, Layout};
use std::ptr;
use criterion::{criterion_group, criterion_main, Criterion, black_box};
/// Мой "потокобезопасный" пул
pub struct ConcurrentPool {
memory: *mut u8,
next: AtomicUsize,
object_size: usize,
capacity: usize,
}
/// Позволяю "ConcurrentPool" распределять данными через потоки
unsafe impl Send for ConcurrentPool {}
unsafe impl Sync for ConcurrentPool {}
impl ConcurrentPool {
pub fn new(object_size: usize, capacity: usize) -> Arc<Self> {
let layout = Layout::array::<u8>(object_size * capacity).unwrap();
let memory = unsafe { alloc(layout) };
Arc::new(Self {
memory,
next: AtomicUsize::new(capacity),
object_size,
capacity,
})
}
#[inline(always)]
pub fn allocate(&self) -> Option<*mut u8> {
let idx = self.next.fetch_sub(1, Ordering::Relaxed);
if idx == 0 {
None
} else {
Some(unsafe { self.memory.add((idx - 1) * self.object_size) })
}
}
}
impl Drop for ConcurrentPool {
fn drop(&mut self) {
unsafe {
dealloc(self.memory, Layout::array::<u8>(self.object_size * self.capacity).unwrap())
};
}
}
/// Бенчмарки
const OBJECT_SIZE: usize = 1024; // 1KB
const OBJECTS_PER_THREAD: usize = 250;
const THREADS: usize = 4;
fn bench_standard_box(c: &mut Criterion) {
c.bench_function("Standard Box 4 threads", |b| {
b.iter(|| {
let mut handles = Vec::with_capacity(THREADS);
for _ in 0..THREADS {
handles.push(std::thread::spawn(move || {
let mut sum = 0;
for i in 0..OBJECTS_PER_THREAD {
let obj = Box::new([i as u8; OBJECT_SIZE]);
sum += obj[0] as usize;
black_box(obj);
}
sum
}));
}
let total: usize = handles.into_iter()
.map(|h| h.join().unwrap())
.sum();
black_box(total);
})
});
}
fn bench_concurrent_pool(c: &mut Criterion) {
let pool = ConcurrentPool::new(OBJECT_SIZE, THREADS * OBJECTS_PER_THREAD);
c.bench_function("ConcurrentPool 4 threads", |b| {
b.iter(|| {
let mut handles = Vec::with_capacity(THREADS);
for _ in 0..THREADS {
let pool = Arc::clone(&pool);
handles.push(std::thread::spawn(move || {
let mut sum = 0;
for i in 0..OBJECTS_PER_THREAD {
if let Some(ptr) = pool.allocate() {
unsafe { ptr::write_bytes(ptr, i as u8, OBJECT_SIZE) };
sum += unsafe { *ptr } as usize;
}
}
sum
}));
}
let total: usize = handles.into_iter()
.map(|h| h.join().unwrap())
.sum();
black_box(total);
})
});
}
criterion_group! {
name = benches;
config = Criterion::default()
.sample_size(1000)
.measurement_time(std::time::Duration::from_secs(10));
targets = bench_standard_box, bench_concurrent_pool
}
criterion_main!(benches);
Программа, доходя до теста с brench_concurrent_box падает с сообщением exit code: 0xc0000005, STATUS_ACCESS_VIOLATION. Я понимаю, что ошибка где-то в unsafe, но никак не могу найти.
Я пробовал сохранять layout: Layout в структуре, есть подозрения на метод new(), но одно могу сказать точно: unsafe в Rust страшнее всякого C.