2015-09-28 7 views
5

私はいくつかのコードを書いています。それは動作します...それは安全ですか?使用する任意のタイプの鋳造の安全

use std::mem; 
use std::ptr; 
use std::marker::PhantomData; 

struct Atomic<T: Copy>(AtomicUsize, PhantomData<T>); 

impl<T: Copy> Atomic<T> { 
    unsafe fn encode(src: T) -> usize { 
     assert!(mem::size_of::<T>() <= mem::size_of::<usize>()); 

     let mut dst = 0; 
     ptr::write(&mut dst as *mut usize as *mut T, src); 
     dst 
    } 

    unsafe fn decode(src: usize) -> T { 
     assert!(mem::size_of::<T>() <= mem::size_of::<usize>()); 
     ptr::read(&src as *const usize as *const T) 
    } 

    fn new(val: T) -> Atomic<T> { 
     unsafe { 
      Atomic(AtomicUsize::new(Self::encode(val)), PhantomData) 
     } 
    } 

    fn load(&self, order: Ordering) -> T { 
     unsafe { Self::decode(self.0.load(order)) } 
    } 

    fn store(&self, val: T, order: Ordering) { 
     unsafe { self.0.store(Self::encode(val), order) } 
    } 
} 

impl<T: Copy + Default> Default for Atomic<T> { 
    fn default() -> Atomic<T> { 
     Self::new(T::default()) 
    } 
} 

あなたが見ることができるように、私はusizeに十分に小さいサイズの任意Copy値を書き込み、そしてAtomicでそれを周りに出荷します。私はそれを新しい価値として読んでいます。

本質的には、サイズsize_of::<usize>()のメモリブロックとしてusizeを使用します。

これが安全であれば、次のステップは、魅力的な操作を検討することです。

unsafe trait PackedInt {} 
unsafe impl PackedInt for u8 {} 
unsafe impl PackedInt for i8 {} 
unsafe impl PackedInt for u32 {} 
unsafe impl PackedInt for i32 {} 
unsafe impl PackedInt for u64 {} 
unsafe impl PackedInt for i64 {} 

impl<T: Copy + PackedInt> Atomic<T> { 
    fn compare_and_swap(&self, current: T, new: T, order: Ordering) -> T { 
     unsafe { 
      Self::decode(self.0.compare_and_swap(
       Self::encode(current), 
       Self::encode(new), 
       order 
      )) 
     } 
    } 

    fn fetch_add(&self, val: T, order: Ordering) -> T { 
     unsafe { 
      Self::decode(self.0.fetch_add(Self::encode(val), order)) 
     } 
    } 

    fn fetch_sub(&self, val: T, order: Ordering) -> T { 
     unsafe { 
      Self::decode(self.0.fetch_sub(Self::encode(val), order)) 
     } 
    } 
} 

これらは、常に、もちろん(2「等しい」の値が原因Tの外ビットに等しくない比較する可能性があるので)オーバーフローに特に賢明ではありませんが、彼らはまだ明確に定義されたように見える...私は思います。

これは安全ですか?その理由は何ですか?

+0

あなたは[安全の錆の定義](http://doc.rust-lang.org/reference.html#behavior-not-considered-unsafe)を参照していますか(関連性のない[未定義の動作](http ://doc.rust-lang.org/reference.html#behavior-considered-undefined))?それとももっと一般的なタイプの「安全な」という意味ですか? – Shepmaster

+0

ほとんどの錆の定義。特に意外ではあるが技術的に安全な問題についてのコメントは素晴らしいことだ。 – Veedrac

答えて

2

ほとんど安全ですが、それほどではありません。あなたはたぶん整数と浮動小数点数でAtomicを使用する人について考えているだけですが、参照もCopyです。ユーザは、緩和された荷物を使用して容易にクラッシュを引き起こし、Atomic<&&u32>に格納することができる。

fetch_addfetch_subは、ビッグエンディアンシステムで正しく機能しません。

+1

*ビッグエンディアンシステム* * CIエコシステム(TravisCIなど)に{x86、ARM、...}、{big、little} -endianをテストする方法がある場合、システムには、私がよく知っている{32,64}ビットのサポートに加えて、 – Shepmaster

+0

良いキャッチ! '' Atomic <'a、T:Copy +' a>(AtomicUsize、PhantomData <(& 'a mut T、T)>) 'を使用すると、最初の問題を修正するようです。 :) – Veedrac

関連する問題