Go言語でプールを作った

https://bitbucket.org/oov/pool/src
http://godoc.org/bitbucket.org/oov/pool

ちょっと探してみたりはしたものの、閉じる時に必要に応じて Close() 呼んでくれるような感じのプールみたいなものが見つからなかったので自分の用途に合わせて作った。

lint に合わせて一応英語っぽいのでコメントも書いてみたけど、そのコメントが英語なのか、英語じゃないのか、それすら私の英語力ではわからないのだ。
これを見てくれ。怯えることはない、こいつはただのサンプルコードだ。
package main

import (
        "bitbucket.org/oov/pool"
        "fmt"
        "io"
        "os"
        "time"
)

type FileFactory string

func (ff FileFactory) New(key interface{}) (interface{}, error) {
        s, ok := key.(string)
        if !ok {
                return nil, fmt.Errorf(";-(")
        }

        return os.Open(string(ff) + "/" + s)
}

func main() {
        pl := pool.New(FileFactory("/etc/nginx"))
        defer pl.Close()

        intf, err := pl.Get("mime.types")
        if err != nil {
                fmt.Println(err)
                return
        }

        f, ok := intf.(*os.File)
        if ok { 
                io.Copy(os.Stdout, f)
        }

        //1秒待つ
        time.Sleep(time.Second)

        //500ミリ秒以上使われていない要素を削除
        pl.Sweep(500*time.Millisecond, false)

        _, ok = pl.GetExists("mime.types")
        if ok { 
                fmt.Println("まだ残ってたよ")
        } else {
                fmt.Println("もう残ってないよ")
        }
}
大体こんな感じのやつ。
  1. pool.Factory インターフェイスを実装した何かを pool.New に渡して *pool.Pool を作成する。
  2. pl.Get された時にキーに対応するものが存在しなければ Factroy.New を呼び出してアイテムを作成して返す。存在する場合は新しく作成せずにそのまま存在するアイテムを返す。
    ちなみに既にアイテムが存在するときだけアイテムを返す pl.GetExists というメソッドもある。
  3. pl.Sweep を手動で呼ぶか、pl.StartWorker を呼んでワーカー goroutine を走らせると最近使われていないアイテムは削除される。
    もしアイテムが io.Closer を実装している場合、アイテムの Close を呼んでから削除される。
    もしアイテムが io.Closer と pool.Closabler を実装している場合、アイテムの Closable が false を返している間は使用中とみなされアイテムは削除されない(ただし pl.Close で全体を閉じる時は Closable の結果を見ずに Close される)。
管理しようとしていた対象の数がそんなに大きくなかったので、削除対象アイテムを探す実装自体は map を総チェックするだけのシンプルなもの。

実際にこのプールを使う時はアイテムが interface{} で渡されてくるとタイプアサーションだらけになって面倒くさいことになるので、もう一枚上から何かを被せるなりする。

ところでこの defer pl.Close() という書き方は戻り値を捨ててててててて若干微妙ですね。
Go言語以外では戻り値を完全に捨てることにあまり抵抗がないのにGo言語だと気になる。
Go言語、いい言語です。