diff --git a/src/SUMMARY.md b/src/SUMMARY.md index 0f6df88..85da0b1 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -38,7 +38,7 @@ * [ポイゾニング](poisoning.md) * [並行性](concurrency.md) * [競合](races.md) - * [Send and Sync](send-and-sync.md) + * [Send と Sync](send-and-sync.md) * [Atomics](atomics.md) * [Vec の実装](vec.md) * [レイアウト](vec-layout.md) diff --git a/src/send-and-sync.md b/src/send-and-sync.md index 959f870..3952694 100644 --- a/src/send-and-sync.md +++ b/src/send-and-sync.md @@ -1,13 +1,30 @@ + +# Send と Sync + + + +すべてのものが継承可変性に従っているわけではありません。いくつかの型においては、 +メモリ上の場所の値を変更している間に、複数のエイリアスを生成することが可能です。 +これらの型が、このアクセスを管理するために同期を行なわない限り、これらは絶対スレッドセーフでは +ありません。 Rust ではこれを、 `Send` トレイトと `Sync` トレイトで表しています。 + +* ある型を他のスレッドに安全に送信できる場合、その型は Send を実装します。 +* ある型をスレッド間で安全に共有できる場合、その型は Sync を実装します (`&T` は Send を実装します) 。 + + + +Send と Sync は Rust の並行性の基本です。したがって、これらが正しく動作するように、 +かなりの量の、特別なツールが存在します。まず真っ先に、これらは[アンセーフなトレイト][unsafe traits]です。 +これはつまり、これらを実装する事はアンセーフで、他のアンセーフなコードが、これらのトレイトが正しく +実装されていると見なすことができます。これらのトレイトは*マーカートレイト* +(これらのトレイトは、メソッドなどの関連情報を備えていません) ですので、正しく実装することは、 +これらのトレイトが、型を実装するものが持っているべき固有の特性を持っているということを +単に意味します。 Send や Sync が正しく実装されていないと、未定義動作を引き起こすことがあります。 + +Send や Sync はまた、自動的に継承されるトレイトでもあります。これはつまり、他のすべてのトレイトとは +違い、もしある型が Send や Sync を実装している型だけで構成されている場合、その型は Send や +Sync を実装することになります。ほとんどすべてのプリミティブ型は Send や Sync を実装しています。 +そして結果的に、あなたが扱うかなり多くの型は、 Send や Sync を実装しています。 + + + +これの主な例外には、このようなものがあります。 + + +* 生ポインタは Send も Sync も実装していません (なぜなら生ポインタには安全性に関するガードが存在しないからです) 。 +* `UnsafeCell` は Sync を実装していません (そしてそれ故に `Cell` も `RefCell` も同様です) 。 +* `Rc` は Send も Sync も実装していません (なぜなら参照カウントが共有され、これは同期されないからです) 。 + +`Rc` と `UnsafeCell` は本当に根本的にスレッドセーフではありません。すなわち、 +これらは、同期されていない共有可変状態を実現できてしまうからです。しかしながら、 +生ポインタは、厳密に言えば、*リント*において、より一層スレッドアンセーフです。 +生ポインタで何か有益なことをしようとすれば、参照外しをする必要があるため、 +もう既にアンセーフです。その意味で、これらをスレッドセーフとしてマークしても +"問題ない" と論じることも可能と言えるでしょう。 + + + +しかしながら、これらの型を含んでいる型が、自動的にスレッドセーフとしてマークされないようにするために、 +これらがスレッドセーフではないということは重要です。これらの型は、些細ではない、そして追跡されない +所有権を持ち、これらを書いた人が、本当にスレッドセーフについて熟考していた、ということは考えにくいです。 +Rc の場合においては、 `*mut` を含んでいる型が絶対にスレッドセーフではない、ということに関する +素晴らしい例があります。 + + +自動的に継承されない型に関しても、必要ならば単純に実装することが可能です。 ```rust struct MyBox(*mut u8); @@ -50,31 +114,56 @@ unsafe impl Send for MyBox {} unsafe impl Sync for MyBox {} ``` + + +*驚くほどに稀*な場合ですが、 Send や Sync が不適切かつ自動的に継承されてしまう +場合があります。このような場合、 Send や Sync の実装を取り払うことも可能です。 ```rust #![feature(optin_builtin_traits)] -// I have some magic semantics for some synchronization primitive! +// プリミティブな型を同期する何か魔法のようなセマンティクスがある! struct SpecialThreadToken(u8); impl !Send for SpecialThreadToken {} impl !Sync for SpecialThreadToken {} ``` + +*それ自体では* Send や Sync を不正に継承してしまうことはありえないということに注意 +してください。他のアンセーフなコードによって特別な意味を持ってしまった型のみが、 +不正な Send や Sync によって問題を引き起こしてしまう可能性があります。 + + + +生ポインタの使用者のほとんどは、 Send や Sync を継承できるよう、十分な抽象化の裏に +生ポインタをカプセル化するべきです。例えば Rust の全ての標準コレクションは、 +アロケーションや複雑な所有権を操るために至るところで生ポインタを使用しているのにも関わらず、 +Send と Sync を実装しています (これらの型が Send と Sync を実装している型を保持している場合) 。 +同じように、これらのコレクションのほとんどのイテレータは、イテレータがコレクションに対して +`&` や `&mut` のように振る舞っているために、 Send や Sync を実装しています。 + + +TODO: 何が Send や Sync を実装できるか、あるいは実装できないかについてのもっと良い +説明。データ競合について述べるだけでも十分? [unsafe traits]: safe-unsafe-meaning.html