Является ли доступ к объекту через один и тот же указатель из нескольких потоков для атомарных операций неопределенным поведением?

#rust

Вопрос:

Поэтому я экспериментирую с некоторыми алгоритмами без блокировки и алгоритмами без ожидания в rust. Я обнаружил, что этот AtomicPtr класс здесь весьма полезен, поскольку он позволяет выполнять такие операции, как cmpexchange или обмен и т.д. Значениями указателей для реализаций структур данных без блокировки. Тем не менее, я прочитал кое-что потенциально касающееся правил Rust о сглаживании указателей :

Нарушение правил сглаживания указателя. amp;mut T и amp;T следуют модели ноалии LLVM, за исключением случаев, когда amp;T содержит незащищенную ячейку. Изменение неизменяемых данных. Все данные внутри элемента const неизменяемы. Кроме того, все данные, полученные с помощью общей ссылки или данных, принадлежащих неизменяемой привязке, являются неизменными, если только эти данные не содержатся в незащищенной ячейке.

Более того, раздел LLVM, на который они ссылаются, также звучит относительно

Это указывает на то, что места в памяти, доступ к которым осуществляется с помощью значений указателя, основанных на аргументе или возвращаемом значении, также недоступны во время выполнения функции с помощью значений указателя, не основанных на аргументе или возвращаемом значении. Эта гарантия распространяется только на места в памяти, которые каким-либо образом изменяются во время выполнения функции.

Из этого не совсем ясно, как атомику можно считать определенным поведением, поскольку атомарная операция изменяет содержимое данных, на которые указывает указатель. Документы Rust и LLVM перечисляют атомику как исключение из этого правила.

Таким образом, я хотел знать, будет ли следующий код считаться неопределенным поведением в Rust:

 struct Point {
    x:AtomicUsize,
    y:AtomicUsize
}

impl Point {
    fn new_ptr() -> *mut Point {
        Box::into_raw(Box::new(Point{x:AtomicUsize::new(0), y:AtomicUsize::new(0)}))
    }
}

fn main() {
    let point = Point::new_ptr();
    let cont = AtomicPtr::new(point);
    let handle = thread::spawn(move || {
        let r = unsafe {  cont.load(Ordering::SeqCst).as_ref().unwrap() };
        for _ in 0..10000 {
            r.x.fetch_add(1, Ordering::SeqCst);
        }
    });
    unsafe {  point.as_ref().unwrap().x.fetch_add(1, Ordering::SeqCst); }
    handle.join().unwrap();
    unsafe {
        drop(Box::from_raw(point));
    }
}
 

Он компилируется и выполняется, как и ожидалось, с небольшими изменениями. Но не уверен, является ли то, что я делаю, неопределенным или нет. Я хочу убедиться, что это разрешенное поведение не будет ограничено или изменено в будущем.

Комментарии:

1. Ваш код не нарушает правила псевдонимов, потому что вы создаете общие ссылки только из указателей. Таким образом, ваш код эквивалентен этому безопасному коду . Атомика «нарушает правила» за счет внутренней изменяемости, используя UnsafeCell внутреннее, что упоминается как исключение в первом процитированном абзаце.

2. Я предполагаю, что вы пытаетесь бороться с моделью сломанной ржавчины ( static требование к сроку службы). Код с mem::transmute может быть лучше.

3. @ensc Нарушает ли правило Rust, изложенное выше, в отношении псевдонимов ? Я знаю правило о ссылках между потоками, но еще более любопытно, считается ли преобразованная ссылка более чем одним псевдонимом или нет.