グローバルナビゲーションへ

本文へ

フッターへ

お役立ち情報Blog



<Goのパッケージ放浪記> ioパッケージに定義されている「Closerインターフェイス」について

今日は前回のWriterインターフェイスに続き、ioパッケージのCloserインターフェイスまわりを覗いていきます。

今回覗いていくソースのバージョンは「go1.14.4」です。

Closerインターフェイス

早速Closerインターフェイスの内容をみていきます。

Closerの初回以降の呼び出しはどうなるか定義されてないようです。

Closerインターフェイスの実装を確認する

Closerインターフェイスの実装として今回は、osパッケージのFileを調査して実装を探っていきます。

まず見ていくファイルは、 os/types.go です。

こちらにFile構造体が定義されています。
 *file にはOS固有の構造体を指定して埋め込み(Embedding)しているようです。

今回はLinux環境での file の定義を見たいので、 os/file_unix.go を見ていきます。
このファイルは冒頭にビルド制約(ビルドタグ)が書かれていて、特定の条件時のみビルド対象にするように設定されています。

 file 構造体の定義は以下の通りです。

pollやepollのファイルディスクリプタと思われる pfd を持っています。
 file 構造体は file 構造体に埋め込まれているので、 file 構造体を通してCloseメソッドを実装しているのがわかります。

 file.pfd のCloseメソッドを呼び出すのがメインの処理です。
こちらも、Reader、Writer同様に同じインターフェイスを呼び出しています。
この先の処理も気になるのですが、今回の目的から反れるのでここまでにしておきます。

失敗した場合の処理は、 pfd が定義されているpollパッケージ内の変数とエラーを比較しています。
 poll.ErrFileClosing と同じ場合は、 e  ErrClosed にしてPathError構造体に入れています。

PathError構造体は以下のようになっています。

行った操作や、発生したパス、エラーを含む構造体です。

最後に、Closeメソッドの成功、失敗にかかわらず SetFinalizer 関数で、すべてのファイナライザをクリアしているのがわかります。

For example, an os.File object could use a finalizer to close the associated operating system file descriptor when a program discards an os.File without calling Close, but it would be a mistake to depend on a finalizer to flush an in-memory I/O buffer such as a bufio.Writer, because the buffer would not be flushed at program exit.

こちらの説明によると、Closeを呼ばなかった場合としてファイナライザを設定しているようです。
明示的にCloseした場合は、保険用のファイナライザが必要なくなるのでクリアしているのかもしれません。
このあたりの挙動は、fileやpollパッケージを覗くときに理解していきたいと思います。

まとめ

Closeメソッド自体はシンプルでしたが、それを取り巻くFile構造体まわりはOSの差分を吸収するために、ビルド制約や構造体の埋め込みを利用して対応しているのが理解できました。
ファイルディスクリプタを扱う部分はシステムコールが多く、理解しにくそうですが追々覗いていきたいと思います。
細かいところでは、エラーの判別やラップの仕方が勉強になりました。


この記事のカテゴリ

FOLLOW US

最新の情報をお届けします