---
# System prepended metadata

title: Important File Writer
tags: [Chromium-General, elkurin-daily-note]

---

# 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の場合はまだ。  

ここで３つの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())
```