# Important File Writer [ImportantFileWriter](https://source.chromium.org/chromium/chromium/src/+/main:base/files/important_file_writer.h;l=40;drc=e5fff99cbb0380ea7f44c60ee15554b6b56320fb)という面白い名前のクラスを見つけたので読んで見る。 ## Overview 時間がかかるが書き込み中のapplication側のCrashが起きた際にコラプトが起きないようにしている。 一方で、システム側のCrashではコラプトが起きうることに注意。 ImportantFileWriterを使うためには、まず[DataSerializer](https://source.chromium.org/chromium/chromium/src/+/main:base/files/important_file_writer.h;l=50;drc=644874afbd6aa7c5de82636ad52d6b298c34a458)または[BackgroundDataSerializer](https://source.chromium.org/chromium/chromium/src/+/main:base/files/important_file_writer.h;l=63;drc=644874afbd6aa7c5de82636ad52d6b298c34a458)を継承したクラスを作り、その中で[ImportantFileWriterのコンストラクタ](https://source.chromium.org/chromium/chromium/src/+/main:base/files/important_file_writer.h;l=88;drc=e5fff99cbb0380ea7f44c60ee15554b6b56320fb)でwriterをつくる。 そこで指定した`path`に書き込むことになる。 読み書きはすべて`task_runner`として指定されたSequencedTaskRunner上で走ることになる。 ```cpp= ImportantFileWriter(const FilePath& path, scoped_refptr<SequencedTaskRunner> task_runner, StringPiece histogram_suffix = StringPiece()); ``` 書き込むには[ScheduleWrite](https://source.chromium.org/chromium/chromium/src/+/main:base/files/important_file_writer.cc;l=341;drc=644874afbd6aa7c5de82636ad52d6b298c34a458)を呼ぶ。 書き込む内容を決めるのは、渡した[DataSerializer](https://source.chromium.org/chromium/chromium/src/+/main:base/files/important_file_writer.h;l=50;drc=644874afbd6aa7c5de82636ad52d6b298c34a458)型オブジェクトの[SerializeData](https://source.chromium.org/chromium/chromium/src/+/main:base/files/important_file_writer.h;l=55;drc=e5fff99cbb0380ea7f44c60ee15554b6b56320fb)の実装部分。例えばEarlyPrefsWriterの例を見てみると[SerializeData](https://source.chromium.org/chromium/chromium/src/+/main:chromeos/ash/components/early_prefs/early_prefs_writer.cc;l=106;drc=644874afbd6aa7c5de82636ad52d6b298c34a458)はJSONWriter::WriteでValue::Dict型の[`root_`](https://source.chromium.org/chromium/chromium/src/+/main:chromeos/ash/components/early_prefs/early_prefs_writer.cc;l=33;drc=644874afbd6aa7c5de82636ad52d6b298c34a458)を書く。 なお、[BackgroundDataSerializer](https://source.chromium.org/chromium/chromium/src/+/main:base/files/important_file_writer.h;l=63;drc=644874afbd6aa7c5de82636ad52d6b298c34a458)はDataSerializerと同じ役割だが、Serializeをstringではなくcallbackを返す形で行う。これはつまり、serializationのlogicをfile I/O をおこなうtask runner上でやってもらうときに使うやつ。 名前の通り、serializationをBackgroundで行ってくれる。 ## 安全なWriteの仕組み [ScheduleWrite](https://source.chromium.org/chromium/chromium/src/+/main:base/files/important_file_writer.cc;l=341;drc=644874afbd6aa7c5de82636ad52d6b298c34a458)を呼ぶと書き込みをscheduleしてくれる。 具体的には、受け取ったserializerロジックを`serializer_`リストに保存して、[`commit_interval_`](https://source.chromium.org/chromium/chromium/src/+/main:base/files/important_file_writer.h;l=209;drc=644874afbd6aa7c5de82636ad52d6b298c34a458)で定められたTimeDeltaごとに[DoScheduleWrite](https://source.chromium.org/chromium/chromium/src/+/main:base/files/important_file_writer.cc;l=368;drc=644874afbd6aa7c5de82636ad52d6b298c34a458)が走る。 そしてSeairlizeDataから[`data`](https://source.chromium.org/chromium/chromium/src/+/main:base/files/important_file_writer.cc;l=377;drc=644874afbd6aa7c5de82636ad52d6b298c34a458)を獲得し、[WriteNowWithBackgroundDataProducer](https://source.chromium.org/chromium/chromium/src/+/main:base/files/important_file_writer.cc;l=317;drc=644874afbd6aa7c5de82636ad52d6b298c34a458)にCallbackという形で受け渡す。このとき、`data`はすでにstring型になって渡されていることに注意。BackgroundDataSerializerの場合はまだ。 ここで3つのCallbackを[ProduceAndWriteStringToFileAtomically](https://source.chromium.org/chromium/chromium/src/+/main:base/files/important_file_writer.cc;l=117;drc=644874afbd6aa7c5de82636ad52d6b298c34a458)に渡す。 このなかで、before_write_callbackで事前処理を指定して、`data`を受け取って[WriteFileAtomicallyImpl](https://source.chromium.org/chromium/chromium/src/+/main:base/files/important_file_writer.cc;l=145;drc=644874afbd6aa7c5de82636ad52d6b298c34a458)で書き込んで、after_write_callbackで事後処理を指定できる。 [WriteFileAtomicallyImpl](https://source.chromium.org/chromium/chromium/src/+/main:base/files/important_file_writer.cc;l=145;drc=644874afbd6aa7c5de82636ad52d6b298c34a458)が実際のファイル処理を行っているパート。 まず[tmp_file](https://source.chromium.org/chromium/chromium/src/+/main:base/files/important_file_writer.cc;l=181;drc=644874afbd6aa7c5de82636ad52d6b298c34a458)という一時ファイルを作る。これは`data`を書き込む先として使用され、データ損失を避けている。 このときtmp fileはターゲットのファイルと同じボリュームにないと一発で動かせない。 ここへの書き込みは[`kMaxWriteAmount`](https://source.chromium.org/chromium/chromium/src/+/main:base/files/important_file_writer.cc;l=191;drc=644874afbd6aa7c5de82636ad52d6b298c34a458)のサイズごとに行われる。全部一気に書こうとするとkernal address-space axhaustionが起きるプラットフォーム(32-bits Windows)があるらしい。 ```cpp= constexpr ptrdiff_t kMaxWriteAmount = 8 * 1024 * 1024; ``` そして書き終えたらtmp file を[Flush](https://source.chromium.org/chromium/chromium/src/+/main:base/files/file_posix.cc;l=564;drc=644874afbd6aa7c5de82636ad52d6b298c34a458)。 Flushの中にMAY_BLOCKで`ScopedBlockingCall`が作られていることからもわかるとおり、ブロッキングコール。 めっちゃ遅いらしい。 ちなみにFlushのlatencyがコメントに乗っていた ```cpp= // Latency percentiles of Flush() across all platforms as of July 2016: // 50 % > 5 ms // 10 % > 58 ms // 1 % > 357 ms // 0.1 % > 1.8 seconds // 0.01 % > 7.6 seconds ``` tmp_fileへの書き込みを終えたら[ReplaceFile](https://source.chromium.org/chromium/chromium/src/+/main:base/files/important_file_writer.cc;l=249;drc=644874afbd6aa7c5de82636ad52d6b298c34a458)でtmp fileとターゲット先を交換。 [ReplaceFile](https://source.chromium.org/chromium/chromium/src/+/main:base/files/file_util_posix.cc;l=431;drc=644874afbd6aa7c5de82636ad52d6b298c34a458)も同様にブロッキングコールで、実体は以下。 ```cpp= rename(from_path.value().c_str(), to_path.value().c_str()) ```